xlua: a very rudimentary K1USN SST contest script

Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
3 files changed, 169 insertions(+), 0 deletions(-)

M xlua/CMakeLists.txt
M xlua/scripts/.hgignore
A => xlua/scripts/contests/K1USN-SST.lua
M xlua/CMakeLists.txt +1 -0
@@ 25,6 25,7 @@ set(LUA_SCRIPTS
 	contests/ARRL-VHF-JUN
 	contests/ARRL-VHF-SEP
 	contests/CQ-WW-VHF
+	contests/K1USN-SST
 	contests/NEQP
 	contests/POTA
 	startup-stats

          
M xlua/scripts/.hgignore +1 -0
@@ 2,6 2,7 @@ syntax: glob
 
 contests/ARRL-VHF-{JAN,JUN,SEP}.{luac,c}
 contests/CQ-WW-VHF.{luac,c}
+contests/K1USN-SST.{luac,c}
 contests/NEQP.{luac,c}
 contests/POTA.{luac,c}
 startup-stats.{luac,c}

          
A => xlua/scripts/contests/K1USN-SST.lua +167 -0
@@ 0,0 1,167 @@ 
+--
+-- Copyright (c) 2023 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy
+-- of this software and associated documentation files (the "Software"), to deal
+-- in the Software without restriction, including without limitation the rights
+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+-- copies of the Software, and to permit persons to whom the Software is
+-- furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+-- SOFTWARE.
+--
+
+-- Rules & info at http://www.k1usn.com/sst.html
+
+local allowed_bands = {
+	[160000] = true,
+	[80000] = true,
+	[40000] = true,
+	[20000] = true,
+	[15000] = true,
+	[10000] = true,
+}
+
+local function hash(qso)
+	return string.format("%s-%u", qso.rx.station_call, qso.rx.band)
+end
+
+local function getmult(qso)
+	if qso.rx.dxcc == 1 or qso.rx.dxcc == 291 then
+		return string.format("ST:%s", qso.rx.state)
+	else
+		return string.format("DX:%u", qso.rx.dxcc)
+	end
+end
+
+local function export_qso(qso, qs, mults)
+	local key = hash(qso)
+	local mult = getmult(qso)
+
+	qs[key] = qso
+	mults[mult] = true
+end
+
+local function export(outfile)
+	local days = {}
+	local mults = {}
+	local qs = {}
+
+	for qso in hlog.index.history(true) do
+		if qso.additional['contest-id'] == 'K1USN-SST' then
+			local day = string.format('%04u-%02u-%02u', qso.start_time.year,
+				qso.start_time.month, qso.start_time.day)
+
+			if mults[day] == nil then
+				mults[day] = {}
+			end
+			if qs[day] == nil then
+				qs[day] = {}
+			end
+
+			export_qso(qso, qs[day], mults[day])
+		end
+	end
+
+	for _, day in ipairs(hlog.utils.sorted_keys(qs)) do
+		local m = mults[day]
+		local q = qs[day]
+
+		local nm = hlog.utils.count_items(m)
+		local nq = hlog.utils.count_items(q)
+
+		print(string.format("K1USN SST %s: %2u Qs * %2u Mults = %u",
+			day, nq, nm, nq * nm))
+	end
+end
+
+local function startup_fill_template(template)
+	print("K1USN SST contest script")
+
+	local state
+	local name
+
+	repeat
+		print()
+		print("Enter name used in contest (e.g., Jeff):")
+
+		name = io.stdin:read()
+		if name == "" then
+			print("Name cannot be empty.")
+			next()
+		else
+			name = name:upper()
+		end
+	until name ~= nil
+
+	repeat
+		print()
+		print("Enter state abbreviation (e.g., MA):")
+
+		state = io.stdin:read()
+		state = state:upper()
+	until state ~= nil
+
+	local exch = string.format("%s %s", name, state)
+
+	print("Using exchange: " .. exch)
+	template.tx.state = state -- FIXME: US/CA only
+	template.additional['contest-id'] = 'K1USN-SST'
+	template.additional['contest-tx'] = exch
+
+	contest.lcd(exch)
+end
+
+local function field_changed(qso, fields, field)
+	local value = fields[field]
+
+	if field == "rx.station_call" then
+		qso.rx.station_call = value
+	elseif field == "rx.name" then
+		qso.rx.name = value
+
+		qso.additional['contest-rx'] = string.format("%s %s", qso.rx.name:upper(), qso.rx.state)
+	elseif field == "rx.state" then
+		value = value:upper()
+		if value ~= "DX" then
+			qso.rx.state = value
+		end
+
+		qso.additional['contest-rx'] = string.format("%s %s", qso.rx.name:upper(), qso.rx.state)
+	elseif field == "comment" then
+		qso.comment = value
+	else
+		error(string.format("Unknown field '%s' encountered", field))
+	end
+end
+
+return {
+	labels = {
+		{ 0,  0, "St. Call" },
+		{ 0, 11, "Name" },
+		{ 0, 25, "St" },
+		{ 0, 28, "Comment" },
+	},
+
+	fields = {
+		{ 1,  0, 10, "rx.station_call", },
+		{ 1, 11, 13, "rx.name", },
+		{ 1, 25,  2, "rx.state", },
+		{ 1, 28, 24, "comment", },
+	},
+
+	events = {
+		export = export,
+		startup = startup_fill_template,
+		field_changed = field_changed,
+	},
+}