feat: Add initial WSPR live data querying scripts
This commit is contained in:
257
js/Base.js
Normal file
257
js/Base.js
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
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 '/js/Utl.js';
|
||||||
|
|
||||||
|
import { Timeline } from '/js/Timeline.js';
|
||||||
|
|
||||||
|
|
||||||
|
// fold this functionality back into the original Event.js later
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
static handlerList = [];
|
||||||
|
|
||||||
|
AddHandler(handler)
|
||||||
|
{
|
||||||
|
Event.handlerList.push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
Emit(evt)
|
||||||
|
{
|
||||||
|
if (typeof(evt) == "string")
|
||||||
|
{
|
||||||
|
evt = {
|
||||||
|
type: evt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const evtHandler of Event.handlerList)
|
||||||
|
{
|
||||||
|
if (evtHandler.OnEvent)
|
||||||
|
{
|
||||||
|
evtHandler.OnEvent(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class Base
|
||||||
|
extends Event
|
||||||
|
{
|
||||||
|
static globalDebugAnyway = false;
|
||||||
|
|
||||||
|
constructor(t)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.AddHandler(this);
|
||||||
|
|
||||||
|
// timeline
|
||||||
|
this.t = t ?? new Timeline();
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// logging levels
|
||||||
|
//
|
||||||
|
|
||||||
|
// debug
|
||||||
|
this.debug = false;
|
||||||
|
|
||||||
|
// info
|
||||||
|
this.info = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only to be called once, from main application, when everything is
|
||||||
|
// constructed and ready to go
|
||||||
|
Run()
|
||||||
|
{
|
||||||
|
urlStateProxy.OnPageLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDebug(tf)
|
||||||
|
{
|
||||||
|
this.debug = tf;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetGlobalDebug(tf)
|
||||||
|
{
|
||||||
|
Base.globalDebugAnyway = tf;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(str)
|
||||||
|
{
|
||||||
|
if (this.debug || Base.globalDebugAnyway)
|
||||||
|
{
|
||||||
|
console.log(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTable(val)
|
||||||
|
{
|
||||||
|
if (this.debug || Base.globalDebugAnyway)
|
||||||
|
{
|
||||||
|
console.table(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetInfo(tf)
|
||||||
|
{
|
||||||
|
this.info = tf;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info(str)
|
||||||
|
{
|
||||||
|
if (this.info)
|
||||||
|
{
|
||||||
|
console.log(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(from, str)
|
||||||
|
{
|
||||||
|
console.log(`ERR: ${from} - ${str}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// A class that does the work of helping components interact with the URL
|
||||||
|
// and the variables captured within the URL.
|
||||||
|
//
|
||||||
|
// Also manages browser history and gives the typical user experience of
|
||||||
|
// how history works, without page reloads.
|
||||||
|
class UrlStateProxy
|
||||||
|
extends Base
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.urlPrior = "";
|
||||||
|
|
||||||
|
window.addEventListener("popstate", (event) => {
|
||||||
|
this.DoUrlSet();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPageLoad()
|
||||||
|
{
|
||||||
|
this.DoUrlSet();
|
||||||
|
this.DoUrlGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
DoUrlSet()
|
||||||
|
{
|
||||||
|
// capture current url and its search params, and parse
|
||||||
|
const url = new URL(window.location);
|
||||||
|
const params = new URLSearchParams(url.search);
|
||||||
|
|
||||||
|
// console.log(`window.location: ${window.location}`)
|
||||||
|
// console.log(`urlIn.origin: ${url.origin}`)
|
||||||
|
// console.log(`urlIn.pathname: ${url.pathname}`)
|
||||||
|
// console.log(`urlIn.search: ${url.search}`)
|
||||||
|
// console.log(`paramsIn : ${params.toString()}`);
|
||||||
|
|
||||||
|
// synchronously send to interested listeners
|
||||||
|
this.Emit({
|
||||||
|
type: "ON_URL_SET",
|
||||||
|
|
||||||
|
Get: (param, defaultValue) => {
|
||||||
|
return utl.GetSearchParam(param, defaultValue);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DoUrlGet()
|
||||||
|
{
|
||||||
|
// synchronously send to interested listeners
|
||||||
|
let paramsOut = new URLSearchParams(``);
|
||||||
|
let evt = {
|
||||||
|
type: "ON_URL_GET",
|
||||||
|
|
||||||
|
Set: (param, value) => {
|
||||||
|
paramsOut.set(param, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
allowBlank: false,
|
||||||
|
};
|
||||||
|
this.Emit(evt);
|
||||||
|
|
||||||
|
const urlIn = new URL(window.location);
|
||||||
|
let paramsIn = new URLSearchParams(urlIn.search);
|
||||||
|
|
||||||
|
// filter out blank parameters unless requested not to
|
||||||
|
if (evt.allowBlank == false)
|
||||||
|
{
|
||||||
|
for (let [key, value] of Array.from(paramsOut.entries()))
|
||||||
|
{
|
||||||
|
if (value === "")
|
||||||
|
{
|
||||||
|
paramsOut.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlOut = new URL(`${urlIn.origin}${urlIn.pathname}?${paramsOut.toString()}`);
|
||||||
|
|
||||||
|
// console.log("")
|
||||||
|
// console.log("")
|
||||||
|
// console.log(`old : ${urlIn.href}`);
|
||||||
|
// console.log(`params : ${paramsOut.toString()}`);
|
||||||
|
// console.log(`new : ${urlOut.href}`);
|
||||||
|
|
||||||
|
let paramsInSorted = new URLSearchParams(paramsIn.toString());
|
||||||
|
paramsInSorted.sort();
|
||||||
|
let pIn = paramsInSorted.toString();
|
||||||
|
|
||||||
|
let paramsOutSorted = new URLSearchParams(paramsOut.toString());
|
||||||
|
paramsOutSorted.sort();
|
||||||
|
let pOut = paramsOutSorted.toString();
|
||||||
|
|
||||||
|
let didNewHistoryEntry = false;
|
||||||
|
if (pIn != pOut)
|
||||||
|
{
|
||||||
|
// console.log("params are different, updating url and history")
|
||||||
|
// console.log(pIn)
|
||||||
|
// console.log(pOut)
|
||||||
|
|
||||||
|
history.pushState({}, "", urlOut.href);
|
||||||
|
|
||||||
|
didNewHistoryEntry = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return didNewHistoryEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnEvent(evt)
|
||||||
|
{
|
||||||
|
switch (evt.type) {
|
||||||
|
case "REQ_URL_GET": this.OnReqUrlGet(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnReqUrlGet()
|
||||||
|
{
|
||||||
|
// the purpose of this is to blow away the forward history when (say)
|
||||||
|
// a user clicks search, which triggers a URL re-evaluation.
|
||||||
|
let didNewHistoryEntry = this.DoUrlGet();
|
||||||
|
|
||||||
|
if (didNewHistoryEntry == false)
|
||||||
|
{
|
||||||
|
// force new history, request came in to re-evaluate, clearly
|
||||||
|
// a change has occurred.
|
||||||
|
history.pushState({}, "", window.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// global single instance
|
||||||
|
let urlStateProxy = new UrlStateProxy();
|
||||||
|
|
||||||
|
|
||||||
378
js/QuerierWsprLive.js
Normal file
378
js/QuerierWsprLive.js
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
/*
|
||||||
|
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 '/js/Utl.js';
|
||||||
|
|
||||||
|
import { Base } from './Base.js';
|
||||||
|
import { WSPR } from '/js/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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user