Files
protoloon/js/QuerierWsprLive.js
2026-04-01 12:22:51 -06:00

379 lines
9.6 KiB
JavaScript

/*
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.)
*/
import * as utl from './Utl.js';
import { Base } from './Base.js';
import { WSPR } from './WSPR.js';
const QUERY_URL_BASE = 'https://db1.wspr.live/';
export class QuerierWsprLive
extends Base
{
constructor()
{
super();
// database is in UTC, enabling this allows queriers to use the
// time of day that makes sense to them, but search will be
// performed in converted UTC
this.autoConvertTimeToUtc = true;
}
async SearchRegularType1(band, min, callsign, timeStart, timeEnd, limit)
{
let query = this.GetSearchRegularType1Query(band, min, callsign, timeStart, timeEnd, limit)
return this.DoQuery(query);
}
async SearchTelemetry(band, min, id1, id3, timeStart, timeEnd, limit)
{
let query = this.GetSearchTelemetryQuery(band, min, id1, id3, timeStart, timeEnd, limit);
return this.DoQuery(query);
}
async SearchRegularType1AnyMinute(band, callsign, timeStart, timeEnd, limit)
{
let query = this.GetSearchRegularType1AnyMinuteQuery(band, callsign, timeStart, timeEnd, limit)
return this.DoQuery(query);
}
// private:
GetDbEnumValForBand(band)
{
band = WSPR.GetDefaultBandIfNotValid(band);
let band__dbBand = new Map();
band__dbBand.set("2190m", -1);
band__dbBand.set("630m", 0);
band__dbBand.set("160m", 1);
band__dbBand.set("80m", 3);
band__dbBand.set("60m", 5);
band__dbBand.set("40m", 7);
band__dbBand.set("30m", 10);
band__dbBand.set("20m", 14);
band__dbBand.set("17m", 18);
band__dbBand.set("15m", 21);
band__dbBand.set("12m", 24);
band__dbBand.set("10m", 28);
band__dbBand.set("6m", 50);
band__dbBand.set("4m", 70);
band__dbBand.set("2m", 144);
band__dbBand.set("70cm", 432);
band__dbBand.set("23cm", 1296);
let retVal = band__dbBand.get(band);
return retVal;
}
async DoQueryRaw(query)
{
// make actual wspr.live query url
let urlWsprLiveMaker = new URL(QUERY_URL_BASE);
urlWsprLiveMaker.searchParams.set("query", query);
let urlWsprLive = urlWsprLiveMaker.href + " FORMAT JSONCompact";
// make debug url
let urlQueryTableMaker = new URL(`/pro/query/`, window.location);
urlQueryTableMaker.searchParams.set("query", query);
let urlQueryTable = urlQueryTableMaker.href;
this.Info(urlQueryTable);
let retVal = {
"queryRequest": {
"query": query,
"queryUrl": urlWsprLive,
},
"queryReply" : {},
"err": "",
};
try {
let response = await fetch(urlWsprLive);
if (response.ok)
{
let queryReply = await response.json();
retVal.queryReply = queryReply;
}
else
{
retVal.err = await response.text();
}
} catch (e) {
console.log("ERR: Query: " + e);
}
return retVal;
}
async DoQuery(query)
{
let queryResultObj = await this.DoQueryRaw(query);
let resultList = [];
// check for errors
if (queryResultObj.err == "")
{
// convert result to convenient object format
// array of objects, eg:
// [
// {
// name: "xxx",
// type: "Float64", // or whatever
// },
// {
// name: "yyy",
// type: "Float64", // or whatever
// }
// ]
//
// These are the column headers of the data returned in a separate array
let fieldMetaList = Array.isArray(queryResultObj.queryReply?.meta)
? queryResultObj.queryReply.meta
: [];
// list of lists
// 0 or more rows, each row containing the number of fields specified in metadata, eg
// [
// [xxxVal, yyyVal],
// [xxxVal, yyyVal],
// ]
let dataRowList = Array.isArray(queryResultObj.queryReply?.data)
? queryResultObj.queryReply.data
: [];
if (!Array.isArray(queryResultObj.queryReply?.data))
{
this.Err("QuerierWsprLive::DoQuery", `Unexpected reply shape for query: ${queryResultObj.queryRequest?.queryUrl ?? query}`);
}
// create a set of objects, each representing one row of results, eg:
// [
// {
// xxx: xxxVal,
// yyy: yyyVal,
// },
// {
// xxx: xxxVal,
// yyy: yyyVal,
// },
// ]
for (let dataRow of dataRowList)
{
if (!Array.isArray(dataRow))
{
continue;
}
let result = {};
for (let i = 0; i < fieldMetaList.length; ++i)
{
result[fieldMetaList[i].name] = dataRow[i];
}
resultList.push(result);
}
}
return resultList;
}
GetSearchRegularType1Query(band, min, callsign, timeStart, timeEnd, limit)
{
if (this.autoConvertTimeToUtc)
{
timeStart = utl.ConvertLocalToUtc(timeStart);
if (timeEnd != "")
{
timeEnd = utl.ConvertLocalToUtc(timeEnd);
}
}
band = WSPR.GetDefaultBandIfNotValid(band);
limit = (limit == undefined || limit < 1) ? 0 : limit;
let dialFreq = WSPR.GetDialFreqFromBandStr(band);
let freqFloor = (dialFreq + 1500 - 100);
let dbBand = this.GetDbEnumValForBand(band);
let timeCriteria = `time between '${timeStart}' and '${timeEnd}'`;
if (timeEnd == "")
{
timeCriteria = `time >= '${timeStart}'`;
}
let query = `
select
time
, toMinute(time) % 10 as min
, tx_sign as callsign
, substring(tx_loc, 1, 4) as grid4
, tx_loc as gridRaw
, power as powerDbm
, rx_sign as rxCallsign
, rx_loc as rxGrid
, frequency
from wspr.rx
where
${timeCriteria}
and band = ${dbBand} /* ${band} */
and min = ${min}
and callsign = '${callsign}'
${this.debug ? ("order by (time, rxCallsign) asc") : "/* order by (time, rxCallsign) asc */"}
${limit ? ("limit " + limit) : ""}
`;
return query;
}
GetSearchTelemetryQuery(band, min, id1, id3, timeStart, timeEnd, limit)
{
if (this.autoConvertTimeToUtc)
{
timeStart = utl.ConvertLocalToUtc(timeStart);
if (timeEnd != "")
{
timeEnd = utl.ConvertLocalToUtc(timeEnd);
}
}
band = WSPR.GetDefaultBandIfNotValid(band);
limit = (limit == undefined || limit < 1) ? 0 : limit;
let dialFreq = WSPR.GetDialFreqFromBandStr(band);
let freqFloor = (dialFreq + 1500 - 100);
let dbBand = this.GetDbEnumValForBand(band);
let timeCriteria = `time between '${timeStart}' and '${timeEnd}'`;
if (timeEnd == "")
{
timeCriteria = `time >= '${timeStart}'`;
}
let query = `
select
time
, substring(tx_sign, 1, 1) as id1
, substring(tx_sign, 3, 1) as id3
, toMinute(time) % 10 as min
, tx_sign as callsign
, tx_loc as grid4
, power as powerDbm
, rx_sign as rxCallsign
, rx_loc as rxGrid
, frequency
from wspr.rx
where
${timeCriteria}
and band = ${dbBand} /* ${band} */
and id1 = '${id1}'
and id3 = '${id3}'
and min = ${min}
and length(callsign) = 6
and length(grid4) = 4
${this.debug ? ("order by (time, rxCallsign) asc") : "/* order by (time, rxCallsign) asc */"}
${limit ? ("limit " + limit) : ""}
`;
return query;
}
GetSearchRegularType1AnyMinuteQuery(band, callsign, timeStart, timeEnd, limit)
{
if (this.autoConvertTimeToUtc)
{
timeStart = utl.ConvertLocalToUtc(timeStart);
if (timeEnd != "")
{
timeEnd = utl.ConvertLocalToUtc(timeEnd);
}
}
band = WSPR.GetDefaultBandIfNotValid(band);
limit = (limit == undefined || limit < 1) ? 0 : limit;
let dialFreq = WSPR.GetDialFreqFromBandStr(band);
let freqFloor = (dialFreq + 1500 - 100);
let dbBand = this.GetDbEnumValForBand(band);
let timeCriteria = `time between '${timeStart}' and '${timeEnd}'`;
if (timeEnd == "")
{
timeCriteria = `time >= '${timeStart}'`;
}
let query = `
select
time
, toMinute(time) % 10 as min
, tx_sign as callsign
, substring(tx_loc, 1, 4) as grid4
, tx_loc as gridRaw
, power as powerDbm
, rx_sign as rxCallsign
, rx_loc as rxGrid
, frequency
from wspr.rx
where
${timeCriteria}
and band = ${dbBand} /* ${band} */
and callsign = '${callsign}'
${this.debug ? ("order by (time, rxCallsign) asc") : "/* order by (time, rxCallsign) asc */"}
${limit ? ("limit " + limit) : ""}
`;
return query;
}
}