1+ package org.operatorfoundation.audiocoder
2+
3+ import androidx.test.ext.junit.runners.AndroidJUnit4
4+ import org.junit.Test
5+ import org.junit.runner.RunWith
6+ import org.junit.Assert.assertArrayEquals
7+ import org.junit.Assert.assertEquals
8+ import org.junit.Assert.assertTrue
9+
10+ @RunWith(AndroidJUnit4 ::class )
11+ class WSPREncoderTest {
12+
13+ @Test
14+ fun testBasicEncodingMatchesJNI () {
15+ val callsign = " W1ABC"
16+ val locator = " FN20"
17+ val power = 30
18+ val offset = 0
19+ val lsb = false
20+
21+ // Kotlin implementation
22+ val kotlinResult = WSPREncoder .encodeToFrequencies(
23+ WSPREncoder .WSPRMessage (callsign, locator, power, offset, lsb)
24+ )
25+
26+ // JNI implementation
27+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
28+ callsign, locator, power, offset, lsb
29+ )
30+
31+ // Should produce identical results
32+ assertEquals(162 , kotlinResult.size)
33+ assertEquals(162 , jniResult.size)
34+ assertArrayEquals(jniResult, kotlinResult)
35+ }
36+
37+ @Test
38+ fun testEncodingWithFrequencyOffset () {
39+ val callsign = " K1JT"
40+ val locator = " FN20"
41+ val power = 23
42+ val offset = 1500
43+ val lsb = false
44+
45+ val kotlinResult = WSPREncoder .encodeToFrequencies(
46+ WSPREncoder .WSPRMessage (callsign, locator, power, offset, lsb)
47+ )
48+
49+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
50+ callsign, locator, power, offset, lsb
51+ )
52+
53+ assertArrayEquals(jniResult, kotlinResult)
54+ }
55+
56+ @Test
57+ fun testLSBModeEncoding () {
58+ val callsign = " N2ABC"
59+ val locator = " EM79"
60+ val power = 37
61+ val offset = 0
62+ val lsb = true
63+
64+ val kotlinResult = WSPREncoder .encodeToFrequencies(
65+ WSPREncoder .WSPRMessage (callsign, locator, power, offset, lsb)
66+ )
67+
68+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
69+ callsign, locator, power, offset, lsb
70+ )
71+
72+ assertArrayEquals(jniResult, kotlinResult)
73+ }
74+
75+ @Test
76+ fun testVariousCallsignFormats () {
77+ val testCases = listOf (
78+ " W1ABC" , // Standard US call
79+ " K1JT" , // Short US call
80+ " AA1A" , // 2-prefix call
81+ " VE3XYZ" , // Canadian call
82+ " G4ABC" , // UK call
83+ " DL1ABC" , // German call
84+ " JA1ABC" // Japanese call
85+ )
86+
87+ val locator = " FN20"
88+ val power = 30
89+
90+ for (callsign in testCases) {
91+ val kotlinResult = WSPREncoder .encodeToFrequencies(
92+ WSPREncoder .WSPRMessage (callsign, locator, power)
93+ )
94+
95+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
96+ callsign, locator, power, 0 , false
97+ )
98+
99+ assertArrayEquals(
100+ " Failed for callsign: $callsign " ,
101+ jniResult,
102+ kotlinResult
103+ )
104+ }
105+ }
106+
107+ @Test
108+ fun testVariousGridLocators () {
109+ val testCases = listOf (
110+ " FN20" , // Northeast US
111+ " EM79" , // Southeast US
112+ " CN87" , // West coast US
113+ " IO91" , // UK
114+ " JN59" , // Central Europe
115+ " PM96" , // Hawaii
116+ " QF22" // New Zealand
117+ )
118+
119+ val callsign = " W1ABC"
120+ val power = 30
121+
122+ for (locator in testCases) {
123+ val kotlinResult = WSPREncoder .encodeToFrequencies(
124+ WSPREncoder .WSPRMessage (callsign, locator, power)
125+ )
126+
127+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
128+ callsign, locator, power, 0 , false
129+ )
130+
131+ assertArrayEquals(
132+ " Failed for locator: $locator " ,
133+ jniResult,
134+ kotlinResult
135+ )
136+ }
137+ }
138+
139+ @Test
140+ fun testVariousPowerLevels () {
141+ val testPowers = listOf (0 , 3 , 7 , 10 , 13 , 17 , 20 , 23 , 27 , 30 , 33 , 37 , 40 , 43 , 47 , 50 , 53 , 57 , 60 )
142+ val callsign = " W1ABC"
143+ val locator = " FN20"
144+
145+ for (power in testPowers) {
146+ val kotlinResult = WSPREncoder .encodeToFrequencies(
147+ WSPREncoder .WSPRMessage (callsign, locator, power)
148+ )
149+
150+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
151+ callsign, locator, power, 0 , false
152+ )
153+
154+ assertArrayEquals(
155+ " Failed for power: $power dBm" ,
156+ jniResult,
157+ kotlinResult
158+ )
159+ }
160+ }
161+
162+ @Test
163+ fun testPowerLevelCorrections () {
164+ val testCases = listOf (1 , 2 , 11 , 12 , 15 , 25 , 35 , 45 , 55 )
165+ val callsign = " W1ABC"
166+ val locator = " FN20"
167+
168+ for (inputPower in testCases) {
169+ val kotlinResult = WSPREncoder .encodeToFrequencies(
170+ WSPREncoder .WSPRMessage (callsign, locator, inputPower)
171+ )
172+
173+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
174+ callsign, locator, inputPower, 0 , false
175+ )
176+
177+ assertArrayEquals(
178+ " Failed for power correction: $inputPower dBm" ,
179+ jniResult,
180+ kotlinResult
181+ )
182+ }
183+ }
184+
185+ @Test
186+ fun testFrequencyRangeCorrectness () {
187+ val message = WSPREncoder .WSPRMessage (
188+ callsign = " W1ABC" ,
189+ locator = " FN20" ,
190+ powerDbm = 30 ,
191+ offsetHz = 0 ,
192+ lsbMode = false
193+ )
194+
195+ val frequencies = WSPREncoder .encodeToFrequencies(message)
196+
197+ val minFreq = frequencies.minOrNull()!!
198+ val maxFreq = frequencies.maxOrNull()!!
199+
200+ assertTrue(minFreq >= 150000 )
201+ assertTrue(maxFreq <= 150450 )
202+ assertTrue(maxFreq > minFreq)
203+ }
204+
205+ @Test
206+ fun testLSBModeInvertsSymbols () {
207+ val callsign = " W1ABC"
208+ val locator = " FN20"
209+ val power = 30
210+
211+ val usbResult = WSPREncoder .encodeToFrequencies(
212+ WSPREncoder .WSPRMessage (callsign, locator, power, 0 , false )
213+ )
214+
215+ val lsbResult = WSPREncoder .encodeToFrequencies(
216+ WSPREncoder .WSPRMessage (callsign, locator, power, 0 , true )
217+ )
218+
219+ var differencesFound = 0
220+ for (i in usbResult.indices) {
221+ if (usbResult[i] != lsbResult[i]) {
222+ differencesFound++
223+ }
224+ }
225+
226+ assertTrue(differencesFound > 0 )
227+ }
228+
229+ @Test
230+ fun testOffsetAddsToAllFrequencies () {
231+ val callsign = " W1ABC"
232+ val locator = " FN20"
233+ val power = 30
234+ val offset = 1000
235+
236+ val noOffsetResult = WSPREncoder .encodeToFrequencies(
237+ WSPREncoder .WSPRMessage (callsign, locator, power, 0 , false )
238+ )
239+
240+ val offsetResult = WSPREncoder .encodeToFrequencies(
241+ WSPREncoder .WSPRMessage (callsign, locator, power, offset, false )
242+ )
243+
244+ for (i in noOffsetResult.indices) {
245+ assertEquals(
246+ noOffsetResult[i] + (offset * 100 ),
247+ offsetResult[i]
248+ )
249+ }
250+ }
251+
252+ @Test
253+ fun testComprehensiveComparison () {
254+ val callsigns = listOf (" W1ABC" , " K1JT" , " VE3XYZ" )
255+ val locators = listOf (" FN20" , " EM79" , " IO91" )
256+ val powers = listOf (10 , 23 , 37 )
257+ val offsets = listOf (0 , 500 , 1500 )
258+ val lsbModes = listOf (false , true )
259+
260+ var testCount = 0
261+ for (callsign in callsigns) {
262+ for (locator in locators) {
263+ for (power in powers) {
264+ for (offset in offsets) {
265+ for (lsb in lsbModes) {
266+ val kotlinResult = WSPREncoder .encodeToFrequencies(
267+ WSPREncoder .WSPRMessage (callsign, locator, power, offset, lsb)
268+ )
269+
270+ val jniResult = CJarInterface .WSPREncodeToFrequencies (
271+ callsign, locator, power, offset, lsb
272+ )
273+
274+ assertArrayEquals(
275+ " Failed: $callsign $locator ${power} dBm offset=${offset} Hz lsb=$lsb " ,
276+ jniResult,
277+ kotlinResult
278+ )
279+ testCount++
280+ }
281+ }
282+ }
283+ }
284+ }
285+
286+ println (" ✓ Ran $testCount comprehensive comparison tests - all passed!" )
287+ }
288+ }
0 commit comments