Commit pirate JS files
This commit is contained in:
447
js/WSPREncoded.js
Normal file
447
js/WSPREncoded.js
Normal file
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
Copyright (c) 2023-forever Douglas Malnati. All rights reserved.
|
||||
|
||||
See the /faq/tos page for details.
|
||||
|
||||
(If this generated header is stamped on a file which is a 3rd party file or under a different license or copyright, then ignore this copyright statement and use that file's terms.)
|
||||
*/
|
||||
|
||||
|
||||
let DEBUG = false;
|
||||
|
||||
function Gather(str)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
console.log(str);
|
||||
}
|
||||
|
||||
return str + "\n";
|
||||
}
|
||||
|
||||
|
||||
export class WSPREncoded
|
||||
{
|
||||
static EnableDebug() { DEBUG = true; }
|
||||
static DisableDebug() { DEBUG = false; }
|
||||
|
||||
static DBM_POWER_LIST = [
|
||||
0, 3, 7,
|
||||
10, 13, 17,
|
||||
20, 23, 27,
|
||||
30, 33, 37,
|
||||
40, 43, 47,
|
||||
50, 53, 57,
|
||||
60
|
||||
];
|
||||
|
||||
static EncodeNumToPower(num)
|
||||
{
|
||||
if (num < 0 || WSPREncoded.DBM_POWER_LIST.length - 1 < num)
|
||||
{
|
||||
num = 0;
|
||||
}
|
||||
|
||||
return WSPREncoded.DBM_POWER_LIST[num];
|
||||
}
|
||||
|
||||
static DecodePowerToNum(power)
|
||||
{
|
||||
let powerVal = WSPREncoded.DBM_POWER_LIST.indexOf(power);
|
||||
powerVal = (powerVal == -1) ? 0 : powerVal;
|
||||
|
||||
return powerVal;
|
||||
}
|
||||
|
||||
static EncodeBase36(val)
|
||||
{
|
||||
let retVal;
|
||||
|
||||
if (val < 10)
|
||||
{
|
||||
retVal = String.fromCharCode("0".charCodeAt(0) + val);
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = String.fromCharCode("A".charCodeAt(0) + (val - 10));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static DecodeBase36(c)
|
||||
{
|
||||
let retVal = 0;
|
||||
|
||||
let cVal = c.charCodeAt(0);
|
||||
|
||||
let aVal = "A".charCodeAt(0);
|
||||
let zVal = "Z".charCodeAt(0);
|
||||
let zeroVal = "0".charCodeAt(0);
|
||||
|
||||
if (aVal <= cVal && cVal <= zVal)
|
||||
{
|
||||
retVal = 10 + (cVal - aVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = cVal - zeroVal;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static DecodeMaidenheadToDeg(grid, opts = {})
|
||||
{
|
||||
let snap = opts.snap ?? "center";
|
||||
|
||||
grid = grid.toUpperCase();
|
||||
|
||||
let lat = 0;
|
||||
let lng = 0;
|
||||
|
||||
if (grid.length >= 2)
|
||||
{
|
||||
let g1 = grid.charAt(0);
|
||||
let g2 = grid.charAt(1);
|
||||
|
||||
lng += (g1.charCodeAt(0) - "A".charCodeAt(0)) * 200000;
|
||||
lat += (g2.charCodeAt(0) - "A".charCodeAt(0)) * 100000;
|
||||
}
|
||||
|
||||
if (grid.length >= 4)
|
||||
{
|
||||
let g3 = grid.charAt(2);
|
||||
let g4 = grid.charAt(3);
|
||||
|
||||
lng += (g3.charCodeAt(0) - "0".charCodeAt(0)) * 20000;
|
||||
lat += (g4.charCodeAt(0) - "0".charCodeAt(0)) * 10000;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (snap == "center")
|
||||
{
|
||||
// snap prior decoded resolution to be in the middle of the grid
|
||||
lng += 200000 / 2;
|
||||
lat += 100000 / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (grid.length >= 6)
|
||||
{
|
||||
let g5 = grid.charAt(4);
|
||||
let g6 = grid.charAt(5);
|
||||
|
||||
lng += (g5.charCodeAt(0) - "A".charCodeAt(0)) * 834;
|
||||
lat += (g6.charCodeAt(0) - "A".charCodeAt(0)) * 417;
|
||||
|
||||
if (snap == "center")
|
||||
{
|
||||
// snap this decoded resolution to be in the middle of the grid
|
||||
lng += 834 / 2;
|
||||
lat += 417 / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (snap == "center")
|
||||
{
|
||||
// snap prior decoded resolution to be in the middle of the grid
|
||||
lng += 20000 / 2;
|
||||
lat += 10000 / 2;
|
||||
}
|
||||
}
|
||||
|
||||
lng -= (180 * 10000);
|
||||
lat -= ( 90 * 10000);
|
||||
|
||||
lng *= 100;
|
||||
lat *= 100;
|
||||
|
||||
lng /= 1000000;
|
||||
lat /= 1000000;
|
||||
|
||||
return [lat, lng];
|
||||
}
|
||||
|
||||
static GetReferenceGrid4(lat, lng)
|
||||
{
|
||||
lat = Number(lat);
|
||||
lng = Number(lng);
|
||||
|
||||
if (isNaN(lat) || isNaN(lng))
|
||||
{
|
||||
throw new RangeError(`Location ${lat}, ${lng} is invalid.`);
|
||||
}
|
||||
|
||||
if (lat < -90 || lat > 90 || lng < -180 || lng > 180)
|
||||
{
|
||||
throw new RangeError(`Location ${lat}, ${lng} is outside valid coordinate bounds.`);
|
||||
}
|
||||
|
||||
if (lat == 90)
|
||||
{
|
||||
lat -= Number.EPSILON;
|
||||
}
|
||||
|
||||
if (lng == 180)
|
||||
{
|
||||
lng -= Number.EPSILON;
|
||||
}
|
||||
|
||||
let lngFromOrigin = lng + 180;
|
||||
let latFromOrigin = lat + 90;
|
||||
|
||||
let fieldLngIdx = Math.floor(lngFromOrigin / 20);
|
||||
let fieldLatIdx = Math.floor(latFromOrigin / 10);
|
||||
|
||||
let squareLngIdx = Math.floor((lngFromOrigin % 20) / 2);
|
||||
let squareLatIdx = Math.floor((latFromOrigin % 10) / 1);
|
||||
|
||||
return ""
|
||||
+ String.fromCharCode("A".charCodeAt(0) + fieldLngIdx)
|
||||
+ String.fromCharCode("A".charCodeAt(0) + fieldLatIdx)
|
||||
+ String.fromCharCode("0".charCodeAt(0) + squareLngIdx)
|
||||
+ String.fromCharCode("0".charCodeAt(0) + squareLatIdx);
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/32806084/google-map-zoom-parameter-in-url-not-working
|
||||
static MakeGoogleMapsLink(lat, lng)
|
||||
{
|
||||
// approx zoom levels
|
||||
// 1: World
|
||||
// 5: Landmass/continent
|
||||
// 10: City
|
||||
// 15: Streets
|
||||
// 20: Buildings
|
||||
let zoom = 4;
|
||||
|
||||
return `https://maps.google.com/?q=${lat},${lng}&ll=${lat},${lng}&z=${zoom}`;
|
||||
}
|
||||
|
||||
static EncodeU4BCall(id1, id3, grid56, altM)
|
||||
{
|
||||
let retVal = "";
|
||||
|
||||
// pick apart inputs
|
||||
let grid5 = grid56.substring(0, 1);
|
||||
let grid6 = grid56.substring(1);
|
||||
|
||||
// convert inputs into components of a big number
|
||||
let grid5Val = grid5.charCodeAt(0) - "A".charCodeAt(0);
|
||||
let grid6Val = grid6.charCodeAt(0) - "A".charCodeAt(0);
|
||||
|
||||
let altFracM = Math.round(altM / 20);
|
||||
|
||||
retVal += Gather(`grid5Val(${grid5Val}), grid6Val(${grid6Val}), altFracM(${altFracM})`);
|
||||
|
||||
// convert inputs into a big number
|
||||
let val = 0;
|
||||
|
||||
val *= 24; val += grid5Val;
|
||||
val *= 24; val += grid6Val;
|
||||
val *= 1068; val += altFracM;
|
||||
|
||||
retVal += Gather(`val(${val})`);
|
||||
|
||||
// extract into altered dynamic base
|
||||
let id6Val = val % 26; val = Math.floor(val / 26);
|
||||
let id5Val = val % 26; val = Math.floor(val / 26);
|
||||
let id4Val = val % 26; val = Math.floor(val / 26);
|
||||
let id2Val = val % 36; val = Math.floor(val / 36);
|
||||
|
||||
retVal += Gather(`id2Val(${id2Val}), id4Val(${id4Val}), id5Val(${id5Val}), id6Val(${id6Val})`);
|
||||
|
||||
// convert to encoded callsign
|
||||
let id2 = WSPREncoded.EncodeBase36(id2Val);
|
||||
let id4 = String.fromCharCode("A".charCodeAt(0) + id4Val);
|
||||
let id5 = String.fromCharCode("A".charCodeAt(0) + id5Val);
|
||||
let id6 = String.fromCharCode("A".charCodeAt(0) + id6Val);
|
||||
let call = id1 + id2 + id3 + id4 + id5 + id6;
|
||||
|
||||
retVal += Gather(`id1(${id1}), id2(${id2}), id3(${id3}), id4(${id4}), id5(${id5}), id6(${id6})`);
|
||||
retVal += Gather(`${call}`);
|
||||
|
||||
retVal = call;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static DecodeU4BCall(call)
|
||||
{
|
||||
let retVal = "";
|
||||
|
||||
// break call down
|
||||
let id2 = call.charAt(1);
|
||||
let id4 = call.charAt(3);
|
||||
let id5 = call.charAt(4);
|
||||
let id6 = call.charAt(5);
|
||||
|
||||
// convert to values which are offset from 'A'
|
||||
let id2Val = WSPREncoded.DecodeBase36(id2);
|
||||
let id4Val = id4.charCodeAt(0) - "A".charCodeAt(0);
|
||||
let id5Val = id5.charCodeAt(0) - "A".charCodeAt(0);
|
||||
let id6Val = id6.charCodeAt(0) - "A".charCodeAt(0);
|
||||
|
||||
retVal += Gather(`id2Val(${id2Val}), id4Val(${id4Val}), id5Val(${id5Val}), id6Val(${id6Val})`);
|
||||
|
||||
// integer value to use to decode
|
||||
let val = 0;
|
||||
|
||||
// combine values into single integer
|
||||
val *= 36; val += id2Val;
|
||||
val *= 26; val += id4Val; // spaces aren't used, so 26 not 27
|
||||
val *= 26; val += id5Val; // spaces aren't used, so 26 not 27
|
||||
val *= 26; val += id6Val; // spaces aren't used, so 26 not 27
|
||||
|
||||
retVal += Gather(`val ${val}`);
|
||||
|
||||
// extract values
|
||||
let altFracM = val % 1068; val = Math.floor(val / 1068);
|
||||
let grid6Val = val % 24; val = Math.floor(val / 24);
|
||||
let grid5Val = val % 24; val = Math.floor(val / 24);
|
||||
|
||||
let altM = altFracM * 20;
|
||||
let grid6 = String.fromCharCode(grid6Val + "A".charCodeAt(0));
|
||||
let grid5 = String.fromCharCode(grid5Val + "A".charCodeAt(0));
|
||||
let grid56 = grid5 + grid6;
|
||||
|
||||
retVal += Gather(`grid ....${grid56} ; altM ${altM}`);
|
||||
retVal += Gather("-----------");
|
||||
|
||||
retVal = [grid56, altM];
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static EncodeU4BGridPower(tempC, voltage, speedKnots, gpsValid)
|
||||
{
|
||||
// parse input presentations
|
||||
tempC = parseFloat(tempC);
|
||||
voltage = parseFloat(voltage);
|
||||
speedKnots = parseFloat(speedKnots);
|
||||
gpsValid = parseInt(gpsValid);
|
||||
|
||||
let retVal = "";
|
||||
|
||||
// map input presentations onto input radix (numbers within their stated range of possibilities)
|
||||
let tempCNum = tempC - -50;
|
||||
let voltageNum = (Math.round(((voltage * 100) - 300) / 5) + 20) % 40;
|
||||
let speedKnotsNum = Math.round(speedKnots / 2.0);
|
||||
let gpsValidNum = gpsValid;
|
||||
|
||||
retVal += Gather(`tempCNum(${tempCNum}), voltageNum(${voltageNum}), speedKnotsNum,(${speedKnotsNum}), gpsValidNum(${gpsValidNum})`);
|
||||
|
||||
// shift inputs into a big number
|
||||
let val = 0;
|
||||
|
||||
val *= 90; val += tempCNum;
|
||||
val *= 40; val += voltageNum;
|
||||
val *= 42; val += speedKnotsNum;
|
||||
val *= 2; val += gpsValidNum;
|
||||
val *= 2; val += 1; // standard telemetry
|
||||
|
||||
retVal += Gather(`val(${val})`);
|
||||
|
||||
// unshift big number into output radix values
|
||||
let powerVal = val % 19; val = Math.floor(val / 19);
|
||||
let g4Val = val % 10; val = Math.floor(val / 10);
|
||||
let g3Val = val % 10; val = Math.floor(val / 10);
|
||||
let g2Val = val % 18; val = Math.floor(val / 18);
|
||||
let g1Val = val % 18; val = Math.floor(val / 18);
|
||||
|
||||
retVal += Gather(`grid1Val(${g1Val}), grid2Val(${g2Val}), grid3Val(${g3Val}), grid4Val(${g4Val})`);
|
||||
retVal += Gather(`powerVal(${powerVal})`);
|
||||
|
||||
// map output radix to presentation
|
||||
let g1 = String.fromCharCode("A".charCodeAt(0) + g1Val);
|
||||
let g2 = String.fromCharCode("A".charCodeAt(0) + g2Val);
|
||||
let g3 = String.fromCharCode("0".charCodeAt(0) + g3Val);
|
||||
let g4 = String.fromCharCode("0".charCodeAt(0) + g4Val);
|
||||
let grid = g1 + g2 + g3 + g4;
|
||||
let power = WSPREncoded.EncodeNumToPower(powerVal);
|
||||
|
||||
retVal += Gather(`grid(${grid}), g1(${g1}), g2(${g2}), g3(${g3}), g4(${g4})`);
|
||||
retVal += Gather(`power(${power})`);
|
||||
|
||||
retVal += Gather(`${grid} ${power}`);
|
||||
|
||||
retVal = [grid, power];
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static DecodeU4BGridPower(grid, power)
|
||||
{
|
||||
let debug = "";
|
||||
|
||||
power = parseInt(power);
|
||||
|
||||
let g1 = grid.charAt(0);
|
||||
let g2 = grid.charAt(1);
|
||||
let g3 = grid.charAt(2);
|
||||
let g4 = grid.charAt(3);
|
||||
|
||||
let g1Val = g1.charCodeAt(0) - "A".charCodeAt(0);
|
||||
let g2Val = g2.charCodeAt(0) - "A".charCodeAt(0);
|
||||
let g3Val = g3.charCodeAt(0) - "0".charCodeAt(0);
|
||||
let g4Val = g4.charCodeAt(0) - "0".charCodeAt(0);
|
||||
let powerVal = WSPREncoded.DecodePowerToNum(power);
|
||||
|
||||
let val = 0;
|
||||
|
||||
val *= 18; val += g1Val;
|
||||
val *= 18; val += g2Val;
|
||||
val *= 10; val += g3Val;
|
||||
val *= 10; val += g4Val;
|
||||
val *= 19; val += powerVal;
|
||||
|
||||
debug += Gather(`val(${val})`);
|
||||
|
||||
let telemetryId = val % 2 ; val = Math.floor(val / 2);
|
||||
let bit2 = val % 2 ; val = Math.floor(val / 2);
|
||||
let speedKnotsNum = val % 42 ; val = Math.floor(val / 42);
|
||||
let voltageNum = val % 40 ; val = Math.floor(val / 40);
|
||||
let tempCNum = val % 90 ; val = Math.floor(val / 90);
|
||||
|
||||
let retVal;
|
||||
if (telemetryId == 0)
|
||||
{
|
||||
let msgType = "extra";
|
||||
let extraTelemSeq = bit2 == 0 ? "first" : "second";
|
||||
|
||||
retVal = {
|
||||
msgType: "extra",
|
||||
msgSeq : extraTelemSeq,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
let msgType = "standard";
|
||||
let gpsValid = bit2;
|
||||
|
||||
let tempC = -50 + tempCNum;
|
||||
let voltage = 3.0 + (((voltageNum + 20) % 40) * 0.05);
|
||||
let speedKnots = speedKnotsNum * 2;
|
||||
let speedKph = speedKnots * 1.852;
|
||||
|
||||
debug += Gather(`tempCNum(${tempCNum}), tempC(${tempC})`);
|
||||
debug += Gather(`voltageNum(${voltageNum}), voltage(${voltage})`);
|
||||
debug += Gather(`speedKnotsNum(${speedKnotsNum}), speedKnots(${speedKnots}), speedKph(${speedKph})`);
|
||||
debug += Gather(`gpsValid(${gpsValid})`);
|
||||
|
||||
debug += Gather(`${tempC}, ${voltage}, ${speedKnots}, ${gpsValid}`);
|
||||
|
||||
retVal = {
|
||||
msgType: msgType,
|
||||
data : [tempC, voltage, speedKnots, gpsValid],
|
||||
};
|
||||
}
|
||||
|
||||
retVal.debug = debug;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user