@@ 28,127 28,66 @@ local allowed_bands = {
[10000] = true,
}
--- DXCC entities that use states (+counties) instead of "DX"
-local uscan_dxcc = {
- [1] = true, -- Canada
- [6] = true, -- Alaska
- [110] = true, -- Hawaii
- [291] = true, -- CONUS
+local mode_points = {
+ ["PH"] = 1,
+ ["CW"] = 2,
+ ["DG"] = 2,
}
local raw_county_data = {
-- NB: these are the CT multiplies starting 2024; previous years
-- used counties instead of Regional Councils of Government
- { 'CT', 'GBR', 'Greater Bridgeport' }, { 'CT', 'LCR', 'Lower CT River' },
- { 'CT', 'NAU', 'Naugatuck Valley' }, { 'CT', 'NOE', 'Northeastern' },
- { 'CT', 'NOW', 'Northwest Hills' }, { 'CT', 'SOE', 'Southeastern' },
- { 'CT', 'SOC', 'South Central' }, { 'CT', 'WES', 'Western' },
+ { 291, 'CT', 'CTGBR', 'Greater Bridgeport' }, { 291, 'CT', 'CTLCR', 'Lower CT River' },
+ { 291, 'CT', 'CTNAU', 'Naugatuck Valley' }, { 291, 'CT', 'CTNOE', 'Northeastern' },
+ { 291, 'CT', 'CTNOW', 'Northwest Hills' }, { 291, 'CT', 'CTSOE', 'Southeastern' },
+ { 291, 'CT', 'CTSOC', 'South Central' }, { 291, 'CT', 'CTWES', 'Western' },
- { 'MA', 'BAR', 'Barnstable' }, { 'MA', 'BER', 'Berkshire' },
- { 'MA', 'BRI', 'Bristol' }, { 'MA', 'DUK', 'Dukes' },
- { 'MA', 'ESS', 'Essex' }, { 'MA', 'FRA', 'Franklin' },
- { 'MA', 'HMD', 'Hampden' }, { 'MA', 'HMP', 'Hampshire' },
- { 'MA', 'MID', 'Middlesex' }, { 'MA', 'NAN', 'Nantucket' },
- { 'MA', 'NOR', 'Norfolk' }, { 'MA', 'PLY', 'Plymouth' },
- { 'MA', 'SUF', 'Suffolk' }, { 'MA', 'WOR', 'Worcester' },
+ { 291, 'MA', 'MABAR', 'Barnstable' }, { 291, 'MA', 'MABER', 'Berkshire' },
+ { 291, 'MA', 'MABRI', 'Bristol' }, { 291, 'MA', 'MADUK', 'Dukes' },
+ { 291, 'MA', 'MAESS', 'Essex' }, { 291, 'MA', 'MAFRA', 'Franklin' },
+ { 291, 'MA', 'MAHMD', 'Hampden' }, { 291, 'MA', 'MAHMP', 'Hampshire' },
+ { 291, 'MA', 'MAMID', 'Middlesex' }, { 291, 'MA', 'MANAN', 'Nantucket' },
+ { 291, 'MA', 'MANOR', 'Norfolk' }, { 291, 'MA', 'MAPLY', 'Plymouth' },
+ { 291, 'MA', 'MASUF', 'Suffolk' }, { 291, 'MA', 'MAWOR', 'Worcester' },
- { 'ME', 'AND', 'Androscoggin' }, { 'ME', 'ARO', 'Aroostook' },
- { 'ME', 'CUM', 'Cumberland' }, { 'ME', 'FRA', 'Franklin' },
- { 'ME', 'HAN', 'Hancock' }, { 'ME', 'KEN', 'Kennebec' },
- { 'ME', 'KNO', 'Knox' }, { 'ME', 'LIN', 'Lincoln' },
- { 'ME', 'OXF', 'Oxford' }, { 'ME', 'PEN', 'Penobscot' },
- { 'ME', 'PIS', 'Piscataquis' }, { 'ME', 'SAG', 'Sagadahoc' },
- { 'ME', 'SOM', 'Somerset' }, { 'ME', 'WAL', 'Waldo' },
- { 'ME', 'WAS', 'Washington' }, { 'ME', 'YOR', 'York' },
+ { 291, 'ME', 'MEAND', 'Androscoggin' }, { 291, 'ME', 'MEARO', 'Aroostook' },
+ { 291, 'ME', 'MECUM', 'Cumberland' }, { 291, 'ME', 'MEFRA', 'Franklin' },
+ { 291, 'ME', 'MEHAN', 'Hancock' }, { 291, 'ME', 'MEKEN', 'Kennebec' },
+ { 291, 'ME', 'MEKNO', 'Knox' }, { 291, 'ME', 'MELIN', 'Lincoln' },
+ { 291, 'ME', 'MEOXF', 'Oxford' }, { 291, 'ME', 'MEPEN', 'Penobscot' },
+ { 291, 'ME', 'MEPIS', 'Piscataquis' }, { 291, 'ME', 'MESAG', 'Sagadahoc' },
+ { 291, 'ME', 'MESOM', 'Somerset' }, { 291, 'ME', 'MEWAL', 'Waldo' },
+ { 291, 'ME', 'MEWAS', 'Washington' }, { 291, 'ME', 'MEYOR', 'York' },
- { 'NH', 'BEL', 'Belknap' }, { 'NH', 'CAR', 'Carroll' },
- { 'NH', 'CHE', 'Cheshire' }, { 'NH', 'COO', 'Coos' },
- { 'NH', 'GRA', 'Grafton' }, { 'NH', 'HIL', 'Hillsborough' },
- { 'NH', 'MER', 'Merrimack' }, { 'NH', 'ROC', 'Rockingham' },
- { 'NH', 'STR', 'Strafford' }, { 'NH', 'SUL', 'Sullivan' },
+ { 291, 'NH', 'NHBEL', 'Belknap' }, { 291, 'NH', 'NHCAR', 'Carroll' },
+ { 291, 'NH', 'NHCHE', 'Cheshire' }, { 291, 'NH', 'NHCOO', 'Coos' },
+ { 291, 'NH', 'NHGRA', 'Grafton' }, { 291, 'NH', 'NHHIL', 'Hillsborough' },
+ { 291, 'NH', 'NHMER', 'Merrimack' }, { 291, 'NH', 'NHROC', 'Rockingham' },
+ { 291, 'NH', 'NHSTR', 'Strafford' }, { 291, 'NH', 'NHSUL', 'Sullivan' },
- { 'RI', 'BRI', 'Bristol' }, { 'RI', 'KEN', 'Kent' },
- { 'RI', 'NEW', 'Newport' }, { 'RI', 'PRO', 'Providence' },
- { 'RI', 'WAS', 'Washington' },
+ { 291, 'RI', 'RIBRI', 'Bristol' }, { 291, 'RI', 'RIKEN', 'Kent' },
+ { 291, 'RI', 'RINEW', 'Newport' }, { 291, 'RI', 'RIPRO', 'Providence' },
+ { 291, 'RI', 'RIWAS', 'Washington' },
- { 'VT', 'ADD', 'Addison' }, { 'VT', 'BEN', 'Bennington' },
- { 'VT', 'CAL', 'Caledonia' }, { 'VT', 'CHI', 'Chittenden' },
- { 'VT', 'ESS', 'Essex' }, { 'VT', 'FRA', 'Franklin' },
- { 'VT', 'GRA', 'Grand Isle' }, { 'VT', 'LAM', 'Lamoille' },
- { 'VT', 'ORA', 'Orange' }, { 'VT', 'ORL', 'Orleans' },
- { 'VT', 'RUT', 'Rutland' }, { 'VT', 'WAS', 'Washington' },
- { 'VT', 'WNH', 'Windham' }, { 'VT', 'WND', 'Windsor' },
+ { 291, 'VT', 'VTADD', 'Addison' }, { 291, 'VT', 'VTBEN', 'Bennington' },
+ { 291, 'VT', 'VTCAL', 'Caledonia' }, { 291, 'VT', 'VTCHI', 'Chittenden' },
+ { 291, 'VT', 'VTESS', 'Essex' }, { 291, 'VT', 'VTFRA', 'Franklin' },
+ { 291, 'VT', 'VTGRA', 'Grand Isle' }, { 291, 'VT', 'VTLAM', 'Lamoille' },
+ { 291, 'VT', 'VTORA', 'Orange' }, { 291, 'VT', 'VTORL', 'Orleans' },
+ { 291, 'VT', 'VTRUT', 'Rutland' }, { 291, 'VT', 'VTWAS', 'Washington' },
+ { 291, 'VT', 'VTWNH', 'Windham' }, { 291, 'VT', 'VTWND', 'Windsor' },
}
--- generate views of the county data
-local map_county_name2abbrev = {}
-local map_county_abbrev2name = {}
-local new_england_states = {}
-
--- initialize county_{name2abbrev,abbrev2name}
-for _, info in pairs(raw_county_data) do
- local a2023 = info[2] .. info[1]
- local a = info[1] .. info[2]
- local n = info[1] .. info[3]
- local s = info[1]
-
- assert(map_county_name2abbrev[n] == nil)
- map_county_name2abbrev[n] = a
-
- -- old (2023) -style abbrevs for easier entry
- assert(map_county_abbrev2name[a2023] == nil)
- map_county_abbrev2name[a2023] = info
- assert(map_county_abbrev2name[a] == nil)
- map_county_abbrev2name[a] = info
-
- new_england_states[s] = true
-end
-
--- dup checking
-local dups = {}
-
-local function mkhash(qso)
+local function duphash(qso, tx, rx)
return string.format("%s-%u-%s-%s-%s",
qso.rx.station_call,
qso.tx.band,
qso.tx.mode,
- qso.additional['contest-tx'],
- qso.additional['contest-rx'])
+ tx,
+ rx)
end
-local function county_name2abbrev(dxcc, state, county)
- -- outside of US or Canada: DX
- if uscan_dxcc[dxcc] == nil then
- return "DX"
- end
-
- -- outside of New England: state/province
- if new_england_states[state] == nil then
- return state
- end
-
- -- inside New England: <state><county> abbreviation
- assert(county ~= nil)
-
- return map_county_name2abbrev[state .. county]
-end
-
-local function county_abbrev2name(abbrev)
- local info = map_county_abbrev2name[abbrev]
-
- -- inside New England: return state & county
- if info ~= nil then
- return info[1], info[3]
- end
-
- -- outside of New England: state
- if abbrev ~= nil and abbrev ~= "DX" then
- return abbrev, nil
- end
-
- -- outside of US
- return nil, nil
-end
+local neqp = hlog.contest_helper.county(raw_county_data, { 1, 291 }, duphash)
local function startup_fill_template(template)
local done = false
@@ 159,19 98,30 @@ local function startup_fill_template(tem
template.tx.dxcc = io.stdin:read()
- if uscan_dxcc[template.tx.dxcc] then
+ if neqp:dxcc_use_state(template.tx.dxcc) then
print("Enter state or province (e.g., MA):")
- local tmp = io.stdin:read()
- local state, county = county_abbrev2name(tmp)
+ local tmp = io.stdin:read():upper()
+ local state, county = neqp:abbrev2name(tmp)
- if county ~= nil then
+ if tmp == "" or tmp == nil then
+ -- try again
+ elseif county ~= nil then
template.tx.state = state
template.tx.county = county
done = true
- elseif new_england_states[tmp] then
+ elseif neqp:state_use_county(template.tx.dxcc, tmp) then
print("Enter county abbreviation (e.g., MID):")
- state, county = county_abbrev2name(tmp .. io.stdin:read())
+
+ local tmp2 = io.stdin:read():upper()
+
+ state, county = neqp:abbrev2name(tmp .. tmp2)
+ if county == nil then
+ -- fall back to just trying the last
+ -- input in case the user entered the
+ -- whole abbreviation (e.g., MAMID)
+ state, county = neqp:abbrev2name(tmp2)
+ end
if county ~= nil then
template.tx.state = state
@@ 190,12 140,6 @@ local function startup_fill_template(tem
-- TODO: fill in qso.tx's {itu,cqz,country,continent}
end
-local function qso_done(qso)
- if allowed_bands[qso.tx.band] then
- dups[mkhash(qso)] = true
- end
-end
-
-- copy country data into QSO
local function sync_zones(qso)
local call = qso.rx.station_call
@@ 235,7 179,7 @@ local function field_changed(qso, fields
qso.additional['contest-rx'] = value
-- set state & county based on the exchange
- local state, county = county_abbrev2name(value)
+ local state, county = neqp:abbrev2name(value)
qso.rx.state = state
qso.rx.county = county
@@ 248,11 192,115 @@ local function field_changed(qso, fields
end
return nil, {
- ["DUP"] = dups[mkhash(qso)] and "red" or "black",
+ ["DUP"] = neqp:is_dup(qso) and "red" or "black",
["BAND"] = allowed_bands[qso.tx.band] and "black" or "red",
}
end
+local function qexchange(side, ex)
+ local ok = true
+ local out = ""
+
+ if side.rst == nil then
+ ok = false
+ out = out .. "59"
+ else
+ out = out .. side.rst
+ end
+
+ if ex == nil then
+ ok = false
+ out = out .. " ??"
+ else
+ out = out .. " " .. ex
+ end
+
+ return out, ok
+end
+
+local function qpoints(qso, tx, rx)
+ assert(qso.tx.mode ~= nil)
+
+ return mode_points[qso.tx.cabrillo_mode]
+end
+
+local function qmult(qso, tx, rx)
+ local function in_ne(side)
+ if not neqp:dxcc_use_county(side.dxcc) and
+ not neqp:dxcc_use_state(side.dxcc) then
+ return false -- DX
+ end
+
+ if not neqp:state_use_county(side.dxcc, side.state) then
+ return false -- state/province outside of New England
+ end
+
+ return true -- inside New England
+ end
+
+ if in_ne(qso.tx) then
+ -- We are in New England; we get to work anyone anywhere.
+ elseif in_ne(qso.rx) then
+ -- We are outside of New England and other station is in New
+ -- England, we get to work them.
+ else
+ -- We are outside of New England and the other station is
+ -- also outside of New England.
+ return nil
+ end
+
+ -- At this point, we know that this contact involves at least one
+ -- New England station.
+
+ if neqp:state_use_county(qso.rx.dxcc, qso.rx.state) then
+ -- Other station should have given us state & county.
+ local state, county = neqp:abbrev2name(rx)
+
+ if county == nil then
+ return nil -- no/bad state & county code given
+ end
+
+ return rx -- state & county code is valid
+ elseif neqp:dxcc_use_state(qso.rx.dxcc) then
+ -- Other station should have given us a state/province.
+ return rx -- TODO: sanity check state/province
+ else
+ -- Other station should have given us "DX".
+ if rx ~= "DX" then
+ return nil -- no/bad exchange
+ end
+
+ return qso.rx.dxcc -- use DXCC as multiplier
+ end
+end
+
+local function export(outfname)
+ local all
+ local station
+ local ops
+ local points
+ local mults
+
+ all, station, ops, points, mults = neqp:export_prep(contest.id, contest.year,
+ qpoints, qmult, qexchange)
+
+ hlog.cabrillo.print_header(contest.id,
+ station,
+ ops:as_array(),
+ points * mults:count_items())
+
+ for _, rec in ipairs(all) do
+ local qso = rec[1]
+ local tx = rec[2]
+ local rx = rec[3]
+ local xqso = not rec[4]
+
+ hlog.cabrillo.print_qso(qso, tx, rx, xqso)
+ end
+
+ hlog.cabrillo.print_footer()
+end
+
return {
labels = {
{ 0, 0, "St. Call" },
@@ 276,6 324,8 @@ return {
},
events = {
+ export = export,
+
-- called once on startup (after config's fill_template)
startup = function(template)
print("NEQP contest script")
@@ 286,7 336,7 @@ return {
if qso.additional['contest-id'] == "NEQP" and
qso.additional['contest-year'] == contest.year and
allowed_bands[qso.tx.band] then
- dups[mkhash(qso)] = true
+ neqp:qso_done(qso)
end
end
end,
@@ 299,23 349,27 @@ return {
local dxcc = qso.tx.dxcc
local state = qso.tx.state
local county = qso.tx.county
- local cs = county_name2abbrev(dxcc, state, county)
+ local exch = neqp:name2abbrev(dxcc, state, county)
-- qso.tx.rst = "59" -- we always send 59
-- qso.rx.rst = "59" -- we expect a 59
- qso.additional['contest-id'] = 'NEQP'
- qso.additional['contest-year'] = contest.year
- if cs == nil then
+ if exch == nil then
contest.lcd("?")
else
- qso.additional['contest-tx'] = cs
- contest.lcd(cs)
+ qso.additional['contest-id'] = 'NEQP'
+ qso.additional['contest-year'] = contest.year
+ qso.additional['contest-tx'] = exch
+ contest.lcd(exch)
end
end,
-- called when QSO is complete & saved, just prior to a reset
- qso_done = qso_done,
+ qso_done = function(qso)
+ if allowed_bands[qso.tx.band] then
+ neqp:qso_done(qso)
+ end
+ end,
-- called whenever a contest field changes
field_changed = field_changed,