Add Support for CWOP
leoherzog opened this issue · 3 comments
leoherzog commented
Add support for sending APRS packets to the Citizen Weather Observer Program. I have no idea how to assemble or send an APRS packet in Apps Script.
leoherzog commented
http://www.wxqa.com/faq.html is supposed to be the documentation, but I found https://www.wxforum.net/index.php?topic=36181.0 way more helpful
leoherzog commented
I believe that something like this would create the packet:
/**
* Given an object with weather station conditions, assemble an APRS packet.
* Thanks to https://www.wxforum.net/index.php?topic=36181.0
* @param {object} Object with keys callsign, latitude, longitude, software, JS Date of observation time, winddir, windspeed, windgust, precipRate, precipTotal, pressure, humidity, luminosity
* @returns {string} APRS packet representation of the conditions
*/
function createAPRSPacket_(conditions) {
if (!conditions.callsign || !conditions.latitude || !conditions.longitude || !conditions.software) {
throw "Missing required parameters. Make sure your object includes at least `callsign`, `latitude`, `longitude`, and `software` keys.";
}
if (!conditions.time) {
console.warn('No `time` parameter specified. Using current time...');
conditions.time = new Date();
}
let time = Utilities.formatDate(new Date(conditions.time), 'UTC', 'd').padStart(2, 0) + Utilities.formatDate(new Date(conditions.time), 'UTC', 'H').padStart(2, 0) + Utilities.formatDate(new Date(conditions.time), 'UTC', 'm').padStart(2, 0);
let lat = conditions.latitude;
if (lat < 0) {
lat = Math.abs(lat);
lat = Math.floor(lat).toString().padStart(2, 0) + (Math.round(60 * parseFloat(lat % 1)*100)/100).toFixed(2).toString().padStart(2, 0) + 'S';
} else {
lat = Math.floor(lat).toString().padStart(2, 0) + (Math.round(60 * parseFloat(lat % 1)*100)/100).toFixed(2).toString().padStart(2, 0) + 'N';
}
let long = conditions.longitude;
if (long < 0) {
long = Math.abs(long);
long = Math.floor(long).toString().padStart(3, 0) + (Math.round(60 * parseFloat(long % 1)*100)/100).toFixed(2).toString().padStart(2, 0) + 'W';
} else {
long = Math.floor(long).toString().padStart(3, 0) + (Math.round(60 * parseFloat(long % 1)*100)/100).toFixed(2).toString().padStart(2, 0) + 'E';
}
let winddir;
if (conditions.winddir) {
winddir = conditions.winddir.toString().padStart(3, 0);
} else {
winddir = '...';
}
let windspeed;
if (conditions.windspeed) {
windspeed = Math.ceil(conditions.windspeed).toString().padStart(3, 0);
} else {
windspeed = '...';
}
let windgust;
if (conditions.windgust) {
windgust = Math.ceil(conditions.windgust).toString().padStart(3, 0);
} else {
windgust = '...';
}
let temp;
if (conditions.temp) {
if (conditions.temp >= 0) {
temp = Math.round(conditions.temp).toString().padStart(3, 0);
} else {
temp = '-' + Number(~~(conditions.temp)).toString().padStart(2, 0);
}
} else {
temp = '...';
}
let precipRate;
if (conditions.precipRate) {
precipRate = (conditions.precipRate * 100).toString().padStart(3, 0);
}
let precipTotal;
if (conditions.precipTotal) {
precipTotal = (conditions.precipTotal * 100).toString().padStart(3, 0);
}
let humidity;
if (conditions.humidity) {
humidity = (conditions.humidity % 100).toString().padStart(2, 0);
}
let pressure; // "altimiter" (QNH) format
if (conditions.pressure) {
pressure = ~~(conditions.pressure / 0.0029529983071445).toString().padStart(5, 0); // bitwise not
}
let luminosity;
if (conditions.luminosity && conditions.luminosity >= 1000) {
luminosity = 'l' + (conditions.luminosity % 1000).toString().padStart(3, 0);
} else if (conditions.luminosity && conditions.luminosity < 1000) {
luminosity = 'L' + conditions.luminosity.toString().padStart(3, 0);
}
let packet = conditions.callsign + '>APRS,TCPIP*:@';
packet += time;
packet += 'z' + lat + '/' + long;
packet += '_' + winddir + '/' + windspeed + 'g' + windgust + 't' + temp;
if (precipRate) packet += 'r' + precipRate;
if (precipTotal) packet += 'p' + precipTotal;
if (humidity) packet += 'h' + humidity;
if (pressure) packet += 'b' + pressure;
if (luminosity) packet += luminosity;
packet += conditions.software;
return packet;
}
Now, trying to figure out how to connect to CWOP via Apps Script
// http://www.wxqa.com/faq.html is supposed to be the documentation, but I found
// https://www.wxforum.net/index.php?topic=36181.0 way more helpful
// More:
// http://www.aprs.org/doc/APRS101.PDF
// http://www.aprs.net/vm/DOS/WX.HTM
function updateCWOP_() {
let station = JSON.parse(CacheService.getScriptCache().get('conditions'));
let conditions = {};
conditions["software"] = station.softwareType;
conditions["callsign"] = cwopStationIDOrHamCallsign;
conditions["latitude"] = station.lat;
conditions["longitude"] = station.lon;
conditions["time"] = station.obsTimeUtc;
conditions["temp"] = station.imperial.temp;
if (station.imperial.windSpeed) conditions["windspeed"] = station.imperial.windSpeed;
if (station.imperial.windGust) conditions["windgust"] = station.imperial.windGust;
if (station.imperial.pressure) conditions["pressure"] = station.imperial.pressure;
if (station.humidity) conditions["humidity"] = station.humidity;
if (station.solarRadiation) conditions["lumosity"] = station.solarRadiation;
console.log(conditions);
let packet = createAPRSPacket_(conditions);
console.log(packet);
try {
// cwop.aprs.net:14580
UrlFetchApp.fetch('cwop.aprs.net:14580', {"method": "post", "payload": packet});
}
catch(e) {
// cwop.aprs.net:23
}
}
leoherzog commented
This is now supported! Read more about this in the v2.2.0 release notes and at cwop.rest.