Virtual HT Transceiver Station
Kanal Siaga Cepat (Presets)
Pilih memori frekuensi untuk langsung berpindah kanal pada radio
Log Transmisi Terkini (Air Traffic)
SQUELCH AUTOTotal Frekuensi
0
Terverifikasi duplex & simplex
Repeater Aktif (RPU)
0
Sub-tone CTCSS Online
Operator Call Sign
0
Siaga Monitor Kanal
Transmisi Hari Ini
48.5m
142 PTT Transmissions
Aktivitas Trafik Transmisi Radio (Per Jam)
Total transmisi PTT pada seluruh kanal frekuensi terdaftar
Spektrum Band
Distribusi VHF vs UHF vs HF
Kanal Frekuensi Terpadat (Channel Load)
Frekuensi dengan durasi pemakaian PTT tertinggi
Master Data Bank Frekuensi HT
Konfigurasi kanal RX/TX, offset repeater (duplex), CTCSS sub-tone, dan power output
| CH | Nama Kanal / Alokasi | RX Freq (MHz) | TX Freq (Offset) | Tone CTCSS | Power | Status | QR Sync | Aksi |
|---|
Direktori Operator & Call Sign
Daftar operator pemegang izin radio komunikasi siaga
Google Apps Script Architecture Suite
Kode lengkap backend Google Apps Script siap salin untuk deploy ke Google Spreadsheet
Fungsi inisialisasi sheet otomatis: DB_CHANNELS, DB_USERS, DB_LOGS, default channel, dan auto-header.
/* ========================================================
FILE 1 — SETUP DATABASE
Fungsi Otomatisasi Google Spreadsheet Database
======================================================== */
function setupDatabase() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. SETUP SHEET CHANNELS
let sheetChannels = ss.getSheetByName('DB_CHANNELS');
if (!sheetChannels) {
sheetChannels = ss.insertSheet('DB_CHANNELS');
}
if (sheetChannels.getLastRow() === 0) {
const headers = ['ID', 'CHANNEL_NO', 'NAME', 'RX_FREQ', 'TX_FREQ', 'CTCSS', 'POWER', 'BAND', 'STATUS', 'NOTES', 'CREATED_AT'];
sheetChannels.appendRow(headers);
sheetChannels.getRange(1, 1, 1, headers.length).setBackground('#2563EB').setFontColor('#FFFFFF').setFontWeight('bold');
// Default Frequencies
sheetChannels.appendRow(['CH-01', '01', 'APRS & CALL LOCAL', '144.390', '144.390', '88.5', 'HIGH', 'VHF', 'ACTIVE', 'National APRS Packets', new Date()]);
sheetChannels.appendRow(['CH-02', '02', 'RAPI LOKAL 01 JAKARTA', '142.500', '142.500', 'None', 'HIGH', 'VHF', 'ACTIVE', 'Calling 10-28', new Date()]);
sheetChannels.appendRow(['CH-03', '03', 'RPU ORARI REGIONAL', '146.820', '146.220', '88.5', 'HIGH', 'VHF', 'ACTIVE', 'Repeater -0.600 MHz', new Date()]);
sheetChannels.appendRow(['CH-04', '04', 'SAR & EMERGENCY FREQ', '143.600', '143.600', 'None', 'HIGH', 'VHF', 'ACTIVE', 'Basarnas Disaster Coord', new Date()]);
sheetChannels.appendRow(['CH-05', '05', 'TACTICAL DISPATCH UHF', '435.000', '430.000', '100.0', 'MID', 'UHF', 'ACTIVE', 'Repeater -5.000 MHz', new Date()]);
}
// 2. SETUP SHEET USERS & OPERATORS
let sheetUsers = ss.getSheetByName('DB_USERS');
if (!sheetUsers) {
sheetUsers = ss.insertSheet('DB_USERS');
}
if (sheetUsers.getLastRow() === 0) {
const userHeaders = ['ID', 'USERNAME', 'PASSWORD', 'CALLSIGN', 'NAME', 'ROLE', 'STATUS', 'CREATED_AT'];
sheetUsers.appendRow(userHeaders);
sheetUsers.getRange(1, 1, 1, userHeaders.length).setBackground('#059669').setFontColor('#FFFFFF').setFontWeight('bold');
sheetUsers.appendRow(['USR-01', 'admin', 'admin123', 'JZ10AERO', 'Super Admin Dispatcher', 'ADMIN', 'ACTIVE', new Date()]);
sheetUsers.appendRow(['USR-02', 'operator1', 'op123', 'YB0AER', 'Radio Operator 1', 'OPERATOR', 'ACTIVE', new Date()]);
}
// 3. SETUP SHEET LOGS
let sheetLogs = ss.getSheetByName('DB_LOGS');
if (!sheetLogs) {
sheetLogs = ss.insertSheet('DB_LOGS');
}
if (sheetLogs.getLastRow() === 0) {
const logHeaders = ['LOG_ID', 'TIMESTAMP', 'CHANNEL_ID', 'CALLSIGN', 'DURATION_SEC', 'NOTES'];
sheetLogs.appendRow(logHeaders);
sheetLogs.getRange(1, 1, 1, logHeaders.length).setBackground('#7C3AED').setFontColor('#FFFFFF').setFontWeight('bold');
}
return { success: true, message: 'Database AeroWave HT berhasil disiapkan!' };
}
Handler web app doGet(), login auth, CRUD endpoint channels, dan export dataset.
/* ========================================================
FILE 3 — CODE.GS
Core Web App Controller & CRUD Google Apps Script
======================================================== */
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('Index')
.setTitle('AeroWave HT Radio Frequency & Dispatcher')
.addMetaTag('viewport', 'width=device-width, initial-scale=1.0')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getDatabaseData() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const chSheet = ss.getSheetByName('DB_CHANNELS');
const userSheet = ss.getSheetByName('DB_USERS');
if (!chSheet) setupDatabase();
const chData = chSheet.getDataRange().getValues();
const chHeaders = chData[0];
const channels = chData.slice(1).map(row => {
let obj = {};
chHeaders.forEach((h, i) => obj[h] = row[i]);
return obj;
});
const userData = userSheet.getDataRange().getValues();
const userHeaders = userData[0];
const users = userData.slice(1).map(row => {
let obj = {};
userHeaders.forEach((h, i) => obj[h] = row[i]);
return obj;
});
return { channels: channels, users: users };
}
function saveChannel(payload) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('DB_CHANNELS');
if (payload.id) {
// Update logic
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] == payload.id) {
sheet.getRange(i+1, 2).setValue(payload.channelNo);
sheet.getRange(i+1, 3).setValue(payload.name);
sheet.getRange(i+1, 4).setValue(payload.rxFreq);
sheet.getRange(i+1, 5).setValue(payload.txFreq);
sheet.getRange(i+1, 6).setValue(payload.ctcss);
sheet.getRange(i+1, 7).setValue(payload.power);
sheet.getRange(i+1, 8).setValue(payload.band);
sheet.getRange(i+1, 9).setValue(payload.status);
return { success: true, message: 'Kanal berhasil diperbarui!' };
}
}
} else {
// Insert new logic
const newId = 'CH-' + Utilities.formatString('%02d', sheet.getLastRow());
sheet.appendRow([newId, payload.channelNo, payload.name, payload.rxFreq, payload.txFreq, payload.ctcss, payload.power, payload.band, payload.status, payload.notes, new Date()]);
return { success: true, message: 'Kanal berhasil ditambahkan!', id: newId };
}
}
function deleteChannel(channelId) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('DB_CHANNELS');
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] == channelId) {
sheet.deleteRow(i + 1);
return { success: true, message: 'Kanal berhasil dihapus!' };
}
}
return { success: false, message: 'Kanal tidak ditemukan!' };
}
Panduan Instalasi Google Apps Script
- Buka Google Spreadsheet baru di browser Anda.
- Pilih menu Extensions (Ekstensi) > Apps Script.
- Salin kode dari FILE 1 (SETUP DATABASE) dan FILE 3 (CODE.GS) ke dalam file
Code.gs. - Buat file HTML baru bernama
Index.html, lalu tempel seluruh kode aplikasi ini. - Jalankan fungsi
setupDatabase()satu kali untuk inisialisasi sheet otomatis. - Klik tombol Deploy > New Deployment > Web app (Access: Anyone).