Skip to content

Instantly share code, notes, and snippets.

@bugobliterator
Last active March 13, 2025 01:19
Show Gist options
  • Select an option

  • Save bugobliterator/88de3076f5d5edaa5542a8ac60992c96 to your computer and use it in GitHub Desktop.

Select an option

Save bugobliterator/88de3076f5d5edaa5542a8ac60992c96 to your computer and use it in GitHub Desktop.
function GPSBand(config)
local self = {
name = "GPS_" .. config.name, -- The name of the band
offset = config.offset, -- The offset of the band
frequencies = config.frequencies, -- The frequencies to test
lowGoodIndex = config.lowGoodIndex, -- The minimum allowed frequency index
highGoodIndex = config.highGoodIndex, -- The maximum allowed frequency index
maxTimeMS = config.maxTimeMS, -- How long to wait for the test to complete
allowedGoodMisses = config.allowedGoodMisses, -- How many misses in the good range should we allow
allowedBadHits = config.allowedBadHits, -- How many hits in the bad range are allowed
minValue = config.minValue, -- The minimum frequency level to be accepted
minGain = config.minGain, -- The min gain level to be accepted
maxGain = config.maxGain, -- The max gain level to be accepted
minGainSeen = config.minGainSeen, -- The highest gain value allowed to be seen at all times
avgFreqValue = {}, -- The average frequency value
num_samples = 0, -- The number of samples seen
minChange = config.minChange, -- The minimum change in frequency to be accepted
};
-- Offset the frequencies
for i = 1, #self.frequencies do
self.frequencies[i] = self.frequencies[i] + self.offset
end
-- setup last gain seen
for i = 1, #self.frequencies do
self.avgFreqValue[i] = 0
end
self.num_samples = 0
return self
end
function GPSTest(gpsBands, expectedFreqTimeMS)
local self = {
gpsBands = gpsBands, -- The GPS bands to test
expectedFreqTimeMS = expectedFreqTimeMS -- The expected time to see a frequency
}
-- Add the runtime variables to the bands
for i = 1, #self.gpsBands do
self.gpsBands[i].startTime = nil -- The time that the test started at
self.gpsBands[i].nextCheck = nil -- The time to check the results
self.gpsBands[i].gpsDetected = nil -- If the GPS signal has been detected
self.gpsBands[i].detectedFrequencies = nil -- The frequencies that have been detected
self.gpsBands[i].freqTime = nil -- The time the frequency has been detected for
self.gpsBands[i].lowestGainSeen = nil -- The lowest gain seen
self.gpsBands[i].gainChangedAt = millis() -- What was the last time a gain change was seen
self.gpsBands[i].lastGainSeen = nil -- What was the last gain seen
end
-- Reset a GPS band test
function self:reset_band(band)
band.startTime = millis()
band.nextCheck = millis() + 10000
band.gpsDetected = false;
if band.freqTime == nil then
band.freqTime = {};
band.detectedFrequencies = {};
end
for i = 1, #band.frequencies do
band.detectedFrequencies[i] = false;
end
end
-- Print out the frequencies for debug purposes
function self:print_gps(band, peakFreqIndex, highestFreqChangeValue, antennaGain)
local builder = "";
for j = 1, #band.frequencies do
if j == band.lowGoodIndex then
builder = builder .. "|";
end
if peakFreqIndex == j then
if j >= band.lowGoodIndex and j <= band.highGoodIndex then
builder = builder .. "[G] ";
else
builder = builder .. "[B] ";
end
elseif band.detectedFrequencies[j] == true then
if j >= band.lowGoodIndex and j <= band.highGoodIndex then
builder = builder .. "G ";
else
builder = builder .. "B ";
end
else
builder = builder .. ". ";
end
if j == band.highGoodIndex then
builder = builder:sub(1, -2) .. "| ";
end
end
send_gcs(band.name .. "-> " .. builder .. " [" .. tostring(peakFreqIndex) .. " " .. tostring(highestFreqChangeValue) ..
" " .. tostring(antennaGain) .. " " .. tostring(band.frequencies[peakFreqIndex]) .. "]");
end
-- Find the current highest frequency.
--- @param seenForMs number The number of milliseconds we have seen this frequency for
--- @return number The index of the highest frequency if found
function self:find_highest_change_in_freq(band, seenForMs)
-- Find the current highest frequency
local peakFreqIndex = nil;
local highestFreqChangeValue = 0;
-- Get all the frequencies with the same antenna gain
local detectedFreq = {}
local antennaGainDetected = nil;
for j = 1, #band.frequencies do
local freqLevel, antennaGain = gps:get_frequency_db(band.frequencies[j]);
if antennaGainDetected == nil then
antennaGainDetected = antennaGain
end
-- If the gain is not the same break
if antennaGain ~= antennaGainDetected then
antennaGainDetected = nil
break;
end
detectedFreq[j] = freqLevel;
end
-- Search for the peak frequency level
if antennaGainDetected ~= nil and antennaGainDetected <= band.maxGain and antennaGainDetected >= band.minGain then
for j = 1, #detectedFreq do
-- Calculate the average frequency value
if detectedFreq[j] ~= nil then
band.avgFreqValue[j] = ((band.avgFreqValue[j] * band.num_samples) + detectedFreq[j]) / (band.num_samples + 1)
if (detectedFreq[j] - band.avgFreqValue[j]) > highestFreqChangeValue and detectedFreq[j] >= band.minValue then
peakFreqIndex = j;
highestFreqChangeValue = detectedFreq[j] - band.avgFreqValue[j];
end
end
end
band.num_samples = band.num_samples + 1
end
if highestFreqChangeValue < band.minChange then
peakFreqIndex = nil;
end
-- if the peak is detected outside the GoodIndex also check that its the overall peak as well
if peakFreqIndex ~= nil and (peakFreqIndex < band.lowGoodIndex or peakFreqIndex > band.highGoodIndex) then
local overallPeak = true;
for j = 1, #detectedFreq do
if detectedFreq[j] ~= nil and detectedFreq[j] > detectedFreq[peakFreqIndex] then
overallPeak = false;
break;
end
end
if overallPeak == false then
peakFreqIndex = nil;
end
end
-- If there was a peak make sure it stays the peak for the required period of time
if peakFreqIndex ~= nil then
if band.freqTime[peakFreqIndex] == nil then
band.freqTime = {};
band.freqTime[peakFreqIndex] = 1;
else
band.freqTime[peakFreqIndex] = band.freqTime[peakFreqIndex] + 1;
end
end
-- Check if any frequencies were seen for long enough
for j = 1, #band.frequencies do
if band.freqTime[j] ~= nil and band.freqTime[j] > calculate_ms(seenForMs) then
band.freqTime = {};
return j, highestFreqChangeValue, antennaGainDetected;
end
end
return nil, nil, antennaGainDetected
end
--- Run the GPS test for a singular band
---@param band any The band to test
function self:test_band(band)
-- Count how long the test has been running for
if band.startTime == nil then
self:reset_band(band);
end
-- Find the frequencies and check that they are in range
local highestFreqIndex, highestFreqChangeValue, antennaGain = self:find_highest_change_in_freq(band, self.expectedFreqTimeMS);
-- Require the gain to stabilize for 3 seconds before we check the readings
if band.lastGainSeen ~= antennaGain then
band.gainChangedAt = millis();
band.lastGainSeen = antennaGain;
end
if band.gainChangedAt + 3000 > millis() then
highestFreqIndex = nil;
end
if antennaGain ~= nil and (band.lowestGainSeen == nil or antennaGain < band.lowestGainSeen) then
band.lowestGainSeen = antennaGain;
end
if highestFreqIndex ~= nil then
band.detectedFrequencies[highestFreqIndex] = true;
band.gpsDetected = true;
self:print_gps(band, highestFreqIndex, highestFreqChangeValue, antennaGain);
-- Check if we have got all the frequencies in the good range
band.goodPass = true;
local goodMissCount = 0;
for j = band.lowGoodIndex, band.highGoodIndex do
if band.detectedFrequencies[j] ~= true then
goodMissCount = goodMissCount + 1;
if goodMissCount > band.allowedGoodMisses then
band.goodPass = false;
break
end
end
end
-- Check if we have got any frequencies in the bad range
band.badPass = true;
local badHitCount = 0;
for j = 1, #band.frequencies do
if j < band.lowGoodIndex or j > band.highGoodIndex then
if band.detectedFrequencies[j] == true then
badHitCount = badHitCount + 1;
if badHitCount >= band.allowedBadHits then
band.badPass = false;
break
end
end
end
end
end
-- Check every 10 seconds for a result
if band.nextCheck < millis() then
local resultCode = "";
local result = "";
band.nextCheck = millis() + 10000;
-- If we have detected all our good frequencies but not our bad ones then pass
if resultCode == "" and (band.goodPass ~= nil and band.badPass ~= nil) then
if band.goodPass == true and band.badPass == true then
resultCode = "PASS";
elseif band.badPass == false then
resultCode = band.name .. "-OGR";
result = "Detected frequencies outside of the good range";
end
end
-- If we can out of time then fail
if resultCode == "" and band.startTime + band.maxTimeMS < millis() then
-- If the lowest gain level is higher than expected then fail
if band.lowestGainSeen ~= nil and band.lowestGainSeen > band.minGainSeen then
resultCode = band.name .. "-BCP";
result = "Lowest gain seen was " .. tostring(band.lowestGainSeen) .. "dB";
elseif band.gpsDetected == false then
resultCode = band.name .. "-NSD";
result = "GPS signal not detected";
else
resultCode = band.name .. "-NFS";
result = "Did not detect the full sweep";
end
end
-- If we have a result code then handle it
if resultCode ~= "" then
if resultCode == "PASS" then
local failureCount = num_test_fails(band.name);
if failureCount > 0 then
if get_test_state_without_codes(band.name) == "" then
fail(band.name, "previously reported " .. tostring(failureCount) .. " failures",
band.name .. "-PBH", false);
end
else
pass(band.name);
end
elseif resultCode ~= "" then
fail(band.name, result, resultCode, true);
end
end
end
end
-- Initialize the GPS test
function self:initialize()
for i = 1, #self.gpsBands do
if add_test(self.gpsBands[i].name) then
add_test_params(self.gpsBands[i].name);
end
end
end
-- Run the GPS test
function self:test()
-- Check if the ublox module is in recovery mode, if so fail the tests
if gps:in_recovery_mode() then
for i = 1, #gpsBands do
-- Fail all the GPS bands with -UBX
fail(gpsBands[i].name, "The ublox is in recovery mode", gpsBands[i].name .. "-UBX", true);
end
else
-- Otherwise run the tests on the bands
for i = 1, #gpsBands do
self:test_band(gpsBands[i]);
end
end
end
return self
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment