KOUKKU Research · White Paper
A pure-Dart, literature-anchored fishing-favourability engine. 12 components. 8 species profiles. Zero AI cost. Sub-millisecond evaluation.
WFI composes twelve independent, literature-anchored component scores into a single 0–100 fishing-favourability number. It runs entirely on-device, in pure Dart, at zero marginal cost — the only scoring layer in Koukku that scales linearly with users without touching the AI budget.
Most fishing apps fail at one or more of three structural steps: reading the right signals, weighting them by what the fish care about, and surfacing the result without overclaiming.
Taking one variable — usually barometric pressure — slapping a "Best Fishing Times" label on it, and calling the job done. A 4 hPa pressure drop over 2 hours is the signature of an arriving frontal boundary; the same drop over 12 hours is a deepening low 200 km to the north. The biological response is very different. WFI splits pressure into three independent components capturing different timescales, each scored and weighted separately.
An opaque ML model wrapped around a heterogeneous training set is indefensible the moment a competent angler asks: what is the model trained on? Whose catch reports? What does the score mean for a species the training set never saw? Every WFI number traces to either a peer-reviewed paper, a documented zero, or an explicit Koukku design constant. There is no opaque layer.
A burbot angler at midnight in February has nearly opposite optimal conditions to an ahven angler at noon in July. Reporting a single "fishing favourability" number is therefore wrong before the maths even begins. The WFI's answer is the species profile: each registered species ships its own 12-element weight vector, its own trapezoidal response curves, and its own circadian peaks.
Conditions are favourable does not mean fish will bite. WFI is an opportunity index: it tells you when conditions reward effort, not when fish are guaranteed to be caught. A WFI of 80 means "the conditions are pulling in your favour"; it does not mean "go reel one in." Section 8 spells this out explicitly.
The WFI's value proposition rests on three architectural decisions. They are mutually reinforcing — a change to any one of them tends to undo the others.
The engine scores one component at a time, then weights by
species. There is no if (species == 'hauki') switch
anywhere in the code. Adding a species cannot regress an existing
one — a new profile is a new constant; the engine path is
unchanged.
Evidence quality is wildly uneven across species. Pike is the best-studied; Baltic herring is inferred from commercial trap-net data. The response: make unevenness legible through weights. Strong literature → nonzero weight with citation. No source → weight pinned to zero with a one-line note. When in doubt, zero it out and document.
Every calculation runs on-device, in pure Dart, with no
dependencies beyond dart:math. Two anglers fishing
the same lake share a single HTTP request per cache window. The
only network dependency is the Open-Meteo fetch that powers every
other weather feature. Zero AI budget consumed.
WFI = round(100 × Σᵢ wᵢ ×
scoreᵢ) with component weights summing to 1.0, enforced as an
assertion across every registered profile. When a component returns
null (missing data), the engine skips it and renormalises
over the present-data weights only.
Each component scores a single forecast-hour attribute into [0.0, 1.0]. A null return means the data is missing; the engine renormalises rather than penalising. Priority reflects the General-profile baseline — exact allocations vary by species profile.
| # | Component | Physical signal | Units | General priority | Key citation |
|---|---|---|---|---|---|
| 1 | pressureTrend |
2 h centred Δ | hPa | High | Stoner (2004); Casselman & Lewis (1996) |
| 2 | pressureChange6h |
6 h backward Δ (pre-frontal) | hPa | Medium | Stoner (2004) |
| 3 | pressureStability24h |
24 h max-min range | hPa | Low | inferred from frontal biology |
| 4 | wind |
sustained 10 m wind speed | m/s | High | Lehtonen & Niemelä (1986) |
| 5 | gustFactor |
gusts ÷ max(sustained, 0.5) | — | Low–Medium (species-specific) | Casselman & Lewis (1996) for hauki |
| 6 | timeOfDay |
true sun-phase circadian (lat/lng-aware) | — | High | Baktoft et al. (2012); Ovidio et al. (1998) |
| 7 | lunar |
illumination % × altitude gate | — | Low–High (species-specific) | Müller (1973); Horppila et al. (2018) |
| 8 | solunarMajorMinor |
proximity to nearest moon transit | minutes | Zeroed in General | Knight (1936) — weak evidence basis |
| 9 | cloud |
cloud cover | % | High | Pierce et al. (2003); Helfman (1981) |
| 10 | airTemp |
2 m air temperature | °C | Medium (proxy for water temp) | Magnuson et al. (1979) |
| 11 | precipitation |
liquid precipitation rate | mm/h | Medium | Steingrimsson & Gislason (2002) for taimen |
| 12 | waterTemp |
resolved water temperature | °C | Highest | Casselman & Lewis (1996); Hofmann & Fischer (2002) |
Each species ships its own 12-element weight vector, its own trapezoidal response curves, and its own circadian peaks. The engine does not branch on species — the same pure-Dart routine consumes whichever profile is supplied.
| Species | Pressure trend | Wind | Time of day | Lunar | Cloud | Water temp | Defining trait |
|---|---|---|---|---|---|---|---|
| General | High | High | High | Low | High | Highest | Cross-species prior |
| Hauki | Highest | High | Crepuscular | Zeroed | High | Medium | Strongest pre-frontal signal in registry |
| Kuha | High | High | Nocturnal/dusk | Highest freshwater | High | Medium | Low-light apex predator |
| Ahven | Highest registry | High | Afternoon peak | Zeroed | Medium | High | Daylight sight-feeder |
| Taimen | Medium | Medium | Crepuscular | Low | Medium | High (cold-skewed) | Light rain bite trigger |
| Lohi | Medium | Medium | Dawn-heavy | Low | Medium | High (cooler plateau) | Weakest pressure support in registry |
| Siika | Medium | High (calm-water) | Midday | Zeroed | Medium | Highest after made | No peer-reviewed barometric study |
| Made | Lowest | Low (depth) | Deep night | Highest registry | High | Highest registry | Depth attenuates all surface signals |
| Silakka | Medium | High (calm-water) | Dusk | Low | High | High (cold-coastal) | Commercial literature only |
Finland's dominant ambush predator. Most heavily studied freshwater piscivore in the temperate northern hemisphere. Thermal plateau 10–22 °C. Carries the highest pressure-trend priority of any species in the registry — the falling-barometer signal is real, well-replicated in Esocid literature, and remains the most robustly documented pre-frontal feeding response we have. Lunar weight is zeroed: Casselman & Lewis (1996) explicitly find no Esocid lunar correlation.
Finland's premier low-light freshwater predator. Tapetum lucidum gives retinal sensitivity 2× pike's; strike rates peak below 1 lux. Undertakes diel vertical migrations to hunt smelt in the upper water column at dusk. Carries the highest lunar priority among freshwater piscivores — bright moonlit nights extend the kuha feeding window and are scored accordingly.
Daylight sight-feeding generalist. Strike rates peak at intermediate light — broken cloud, not full overcast or full glare. Diel pattern daytime-dominant (peak 11:00–17:00 local). Carries the highest pressure-trend priority in the entire registry — the Stoner (2004) percid review places perch among the strongest barometric responders. Both lunar and solunar components are zeroed: no peer-reviewed support found for either in this species.
Cold-stenothermal salmonid. Thermal preferendum 12.2 °C. Light rain is a documented bite trigger — drift availability spikes are cited by Steingrimsson & Gislason (2002). Pressure response essentially neutral: the angler-tradition "rising pressure triggers trout" rule has no peer-reviewed corroboration (Stoner 2004). Carries the highest precipitation priority of any profiled species.
Cooler than taimen; thermal preferendum ~14 °C. Adult migration arrest above 22 °C (Solomon & Sambrook 2004). Feeding peaks at low surface light. Dawn-heavy diel pattern. The pressure-trend allocation is the most uncertain in the registry — no salmonid barometric signal has been cleanly replicated in peer-reviewed work — and is flagged as a Koukku-calibration candidate.
Cold-water planktivore. Sustained feeding plateau 7–14 °C — the coldest in the registry. Calm-water specialist: surface chop above 4 m/s disrupts thermocline stability and shoal depth predictability. Pressure response is entirely unstudied — no peer-reviewed coregonid barometric study has been found. Carries the highest water-temperature priority of any species after made. Barometric allocation flagged as a calibration candidate.
Cold-water benthic specialist living at depths of 10–80 m. Carries the highest water-temperature priority in the entire registry — water temp is the dominant driver of burbot activity. Also carries the highest lunar priority in the registry — Müller (1973) and Harrison et al. (2013) both anchor burbot's entrainment to the dark half of the day. Surface pressure signals attenuate with depth; three components are zeroed entirely.
#1 Finnish coastal angler catch by volume. Calm-water shoaling specialist — commercial trap-net data (Casini et al. 2006; LUKE 2018) shows catches drop sharply above 4 m/s. Dusk-dominant diel pattern. The weakest recreational-angler literature coverage in the registry; most sources are commercial-fishery (ICES WGBFAS, LUKE) or general clupeid biology. All allocations flagged as calibration candidates.
When the user hasn't told Koukku what they're fishing for, the WFI uses the General profile — a deliberate prior, not an average of species profiles.
The General profile is a prior probability distribution over the space of Finnish angling sessions. It weights components by how reliably predictive they are cross-species, not by what any single species most cares about. Water temperature carries the highest single allocation — it is the most-cited environmental variable in fish metabolic physiology. Solunar proximity carries no allocation at all — the species-neutral solunar literature is angler-tradition, not peer-reviewed ecology.
What General does not do: it is not the mean of all species profiles. Taking the mean would produce a ghost solunar weight with no clear biological interpretation. The General profile honours the relief-valve discipline — zero with documentation — as strictly as any species profile.
The v2-26 General profile was rebalanced alongside the expansion from 8 to 12 components. The full pressure family (trend + 6 h + 24 h) was trimmed as a group. The trim reflects the Stoner (2004) effect-size caveat: angler tradition over-weights barometric signals, and the General baseline is the appropriate place to push back gently against that bias. The freed allocation was redistributed to lunar, air temperature, and water temperature components.
Three stages of evidence quality, only Stage 1 currently active. The architecture was designed from day one to support Stages 2 and 3 without refactoring the engine.
Every non-zero weight cites peer-reviewed literature in the source
comment. Every zero weight names the absent citation. The
per-catch wfi_version column stamps every catch with
the engine version so historical analytics remain queryable across
future refactors. Current stage.
When per-species catch counts reach 1 000 with metadata
(timestamp, lat/lng, weather snapshot), Koukku can compute a
posterior probability distribution over WFI weights conditioned on
observed CPUE. The architecture is ready: wfi_version
is already stored; the literature priors are the Stage 2 starting
point.
With sufficient per-user catch data, the General profile can be personalised. A user whose hauki bites historically correlate with solunar transits could legitimately receive a higher solunar allocation than the General prior. Years away; requires the user base and data density to justify the added complexity.
Explicitly stated so no Koukku user, biologist, or partner is left with an incorrect impression.
solunarMajorMinor exists and contributes for some
species, but the General profile zeroes it out and most species
keep it at very low priority. WFI is not built around the
lunar-transit hypothesis.
A concrete scoring walkthrough for a specific forecast hour — the canonical hauki scenario. The bar lengths show each component's contribution relative to the highest-scoring component in this hour.
pressureTrendpressureChange6hcloudwindgustFactorwaterTemppressureStability24hsolunarMajorMinortimeOfDayairTempprecipitationlunar
Same hour scored against ahven profile → 77 / 100. The dominant
positive is the pressure-trend component. The dominant negative is
timeOfDay — pre-dawn is not yet the ahven's
mid-afternoon optimum. This divergence illustrates exactly why a
species-neutral "WFI" score would be meaningless: the same hour
reads very differently to a pike angler vs a perch angler.
Selected peer-reviewed citations that anchor the WFI scoring architecture. Every non-zero weight in every species profile traces to one of these (or a companion listed in the full white paper).