PPM control for NDF fractional control

This commit is contained in:
Chris Frankland-Wright 2025-10-28 16:24:44 +00:00
parent 2e8bc9ac5e
commit 2295a29d75
11 changed files with 532 additions and 20 deletions

View file

@ -95,11 +95,22 @@
</div>
</div>
</div>
<div class="control-group">
<input type="checkbox" id="auto-ppm-enabled" name="auto-ppm-enabled" style="vertical-align: middle;">
<label for="auto-ppm-enabled" style="vertical-align: middle;">Enable Fractional NDF Discipline (auto PPM)</label>
<label for="ppm-target" style="margin-left: 10px;">Target PPM:</label>
<input type="number" id="ppm-target" style="width: 80px;" step="1">
</div>
<div class="control-group">
<button id="save-config">Save Timeturner Config</button>
<button id="manual-sync">Send Manual Sync</button>
<span id="sync-message"></span>
</div>
<div class="control-group">
<input type="checkbox" id="ppm-applied" disabled>
<label for="ppm-applied">PPM Applied</label>
<span id="fractional-ndf-badge" style="display:none; font-weight:bold; color:#28a745; margin-left: 10px;">Fractional NDF discipline active</span>
</div>
<div class="control-group" style="display: none;">
<label>Nudge Clock (ms):</label>
<button id="nudge-down">-</button>

View file

@ -95,11 +95,22 @@
</div>
</div>
</div>
<div class="control-group">
<input type="checkbox" id="auto-ppm-enabled" name="auto-ppm-enabled" style="vertical-align: middle;">
<label for="auto-ppm-enabled" style="vertical-align: middle;">Enable Fractional NDF Discipline (auto PPM)</label>
<label for="ppm-target" style="margin-left: 10px;">Target PPM:</label>
<input type="number" id="ppm-target" style="width: 80px;" step="1">
</div>
<div class="control-group">
<button id="save-config">Save Config</button>
<button id="manual-sync">Manual Sync</button>
<span id="sync-message"></span>
</div>
<div class="control-group">
<input type="checkbox" id="ppm-applied" disabled>
<label for="ppm-applied">PPM Applied</label>
<span id="fractional-ndf-badge" style="display:none; font-weight:bold; color:#28a745; margin-left: 10px;">Fractional NDF discipline active</span>
</div>
<div class="control-group">
<label>Nudge Clock (ms):</label>
<button id="nudge-down">-</button>

View file

@ -14,6 +14,7 @@ const mockApiDataSets = {
timecode_delta_frames: 0.125,
jitter_status: 'GOOD',
interfaces: ['192.168.1.100/24 (eth0)', '10.0.0.5/8 (wlan0)'],
fractional_ndf_discipline_active: false,
},
config: {
hardwareOffsetMs: 10,
@ -41,6 +42,7 @@ const mockApiDataSets = {
timecode_delta_frames: 0.075,
jitter_status: 'GOOD',
interfaces: ['192.168.1.100/24 (eth0)'],
fractional_ndf_discipline_active: false,
},
config: {
hardwareOffsetMs: 10,
@ -64,6 +66,7 @@ const mockApiDataSets = {
timecode_delta_frames: -12.5,
jitter_status: 'AVERAGE',
interfaces: ['192.168.1.100/24 (eth0)'],
fractional_ndf_discipline_active: true,
},
config: {
hardwareOffsetMs: 10,
@ -87,6 +90,7 @@ const mockApiDataSets = {
timecode_delta_frames: 20,
jitter_status: 'AVERAGE',
interfaces: ['192.168.1.100/24 (eth0)'],
fractional_ndf_discipline_active: false,
},
config: {
hardwareOffsetMs: 10,
@ -110,6 +114,7 @@ const mockApiDataSets = {
timecode_delta_frames: 93076,
jitter_status: 'GOOD',
interfaces: ['192.168.1.100/24 (eth0)'],
fractional_ndf_discipline_active: false,
},
config: {
hardwareOffsetMs: 10,
@ -133,6 +138,7 @@ const mockApiDataSets = {
timecode_delta_frames: 0.25,
jitter_status: 'BAD',
interfaces: ['192.168.1.100/24 (eth0)'],
fractional_ndf_discipline_active: false,
},
config: {
hardwareOffsetMs: 10,
@ -156,6 +162,7 @@ const mockApiDataSets = {
timecode_delta_frames: 0,
jitter_status: 'UNKNOWN',
interfaces: [],
fractional_ndf_discipline_active: false,
},
config: {
hardwareOffsetMs: 0,

View file

@ -25,6 +25,9 @@
const hwOffsetInput = document.getElementById('hw-offset');
const autoSyncCheckbox = document.getElementById('auto-sync-enabled');
const autoPpmCheckbox = document.getElementById('auto-ppm-enabled');
const ppmTargetInput = document.getElementById('ppm-target');
const ppmAppliedCheckbox = document.getElementById('ppm-applied');
const offsetInputs = {
h: document.getElementById('offset-h'),
m: document.getElementById('offset-m'),
@ -35,6 +38,7 @@
const saveConfigButton = document.getElementById('save-config');
const manualSyncButton = document.getElementById('manual-sync');
const syncMessage = document.getElementById('sync-message');
const fractionalNdfBadge = document.getElementById('fractional-ndf-badge');
const nudgeDownButton = document.getElementById('nudge-down');
const nudgeUpButton = document.getElementById('nudge-up');
@ -150,6 +154,18 @@
} else {
statusElements.interfaces.textContent = 'No active interfaces found.';
}
// Show/hide fractional NDF discipline badge
if (fractionalNdfBadge) {
if (data.fractional_ndf_discipline_active) {
fractionalNdfBadge.style.display = 'inline';
} else {
fractionalNdfBadge.style.display = 'none';
}
}
if (ppmAppliedCheckbox) {
ppmAppliedCheckbox.checked = !!data.fractional_ndf_discipline_active;
}
}
function animateClocks() {
@ -238,6 +254,8 @@
const data = mockApiDataSets[currentMockSetKey].config;
hwOffsetInput.value = data.hardwareOffsetMs;
autoSyncCheckbox.checked = data.autoSyncEnabled;
if (autoPpmCheckbox) autoPpmCheckbox.checked = !!data.autoFractionalPpmEnabled;
if (ppmTargetInput && typeof data.fractionalPpmTarget === 'number') ppmTargetInput.value = data.fractionalPpmTarget;
offsetInputs.h.value = data.timeturnerOffset.hours;
offsetInputs.m.value = data.timeturnerOffset.minutes;
offsetInputs.s.value = data.timeturnerOffset.seconds;
@ -252,6 +270,8 @@
const data = await response.json();
hwOffsetInput.value = data.hardwareOffsetMs;
autoSyncCheckbox.checked = data.autoSyncEnabled;
if (autoPpmCheckbox) autoPpmCheckbox.checked = !!data.autoFractionalPpmEnabled;
if (ppmTargetInput && typeof data.fractionalPpmTarget === 'number') ppmTargetInput.value = data.fractionalPpmTarget;
offsetInputs.h.value = data.timeturnerOffset.hours;
offsetInputs.m.value = data.timeturnerOffset.minutes;
offsetInputs.s.value = data.timeturnerOffset.seconds;
@ -268,6 +288,8 @@
hardwareOffsetMs: parseInt(hwOffsetInput.value, 10) || 0,
autoSyncEnabled: autoSyncCheckbox.checked,
defaultNudgeMs: parseInt(nudgeValueInput.value, 10) || 0,
autoFractionalPpmEnabled: autoPpmCheckbox ? autoPpmCheckbox.checked : undefined,
fractionalPpmTarget: ppmTargetInput ? (parseInt(ppmTargetInput.value, 10) || 0) : undefined,
timeturnerOffset: {
hours: parseInt(offsetInputs.h.value, 10) || 0,
minutes: parseInt(offsetInputs.m.value, 10) || 0,
@ -277,6 +299,10 @@
}
};
// Remove undefined keys if controls are absent
if (config.autoFractionalPpmEnabled === undefined) delete config.autoFractionalPpmEnabled;
if (config.fractionalPpmTarget === undefined) delete config.fractionalPpmTarget;
if (useMockData) {
console.log('Mock save:', config);
alert('Configuration saved (mock).');