Skip to content

Commit 131edbe

Browse files
committed
WSPREncodeToFrequencies
1 parent 711e134 commit 131edbe

File tree

2 files changed

+124
-1
lines changed

2 files changed

+124
-1
lines changed

AudioCoder/src/main/java/org/operatorfoundation/audiocoder/CJarInterface.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ public class CJarInterface {
66
System.loadLibrary("QuietScream");
77
}
88

9+
/**
10+
* Encodes WSPR messages into frequency data for direct radio hardware control.
11+
*
12+
* @param callsign Amateur radio callsign
13+
* @param locator Maidenhead grid square locator
14+
* @param power Power level in dBm (0-60)
15+
* @param offset Frequency offset in Hz (added to 1500 Hz base)
16+
* @param lsb LSB mode - inverts symbol order if true
17+
* @return byte array containing 162 frequencies as 64-bit integers (freq * 100)
18+
* Array size: 1,296 bytes (162 symbols × 8 bytes each)
19+
*/
20+
public static native byte[] WSPREncodeToFrequencies(String callsign, String locator, int power, int offset, boolean lsb);
21+
922
public static native byte[] WSPREncodeToPCM(String callsign, String locator, int power, int offset, boolean lsb);
1023

1124
public static native WSPRMessage[] WSPRDecodeFromPcm(byte[] sound, double dialfreq, boolean lsb);

AudioCoder/src/main/jni/libloud.cpp

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Java_org_operatorfoundation_audiocoder_CJarInterface_WSPREncodeToPCM
5757
// Frequency spacing between the symbols - 1.4548
5858
double frequency = 1500 + ((int) j_offset) + symbols[i] * 1.4548;
5959

60-
// TODO: Create a new function that converts frequency (double) to ints ( * 100 + casting to UInt64) to Bytes and returns a byte array of the frequencies
60+
// TODO: Create a new version of this function that converts frequency (double) to ints ( * 100 + casting to UInt64) to Bytes and returns a byte array of the frequencies
6161
// Frequency array size = # of symbols * 8 bytes (size of 64 bit integer)
6262
double theta = frequency * TAU / (double) 12000;
6363
// 'volume' is UInt16 with range 0 thru Uint16.MaxValue ( = 65 535)
@@ -80,6 +80,116 @@ Java_org_operatorfoundation_audiocoder_CJarInterface_WSPREncodeToPCM
8080
return ret;
8181
}
8282

83+
/**
84+
* WSPR Frequency Encoder
85+
*
86+
* Encodes WSPR message into an array of frequencies that can be sent directly to custom radio hardware.
87+
*
88+
* @param env JNI environment pointer
89+
* @param cls Java class reference
90+
* @param j_calls Callsign string
91+
* @param j_local Grid square locator
92+
* @param j_powr Power level in dbm (0-60)
93+
* @param j_offset Frequency offset in Hz (added to base 1500 Hz)
94+
* @param lsb_mode LSB mode flag - inverts symbol order if true
95+
*
96+
* @return jbyteArray containing 162 frequencies as 64-bit integers (* 100)
97+
* Total array size: 162 symbols * 8 bytes = 1,296 bytes
98+
* Each frequency is stored as big-endien 64-bit integer with 0.01 Hz precision
99+
*/
100+
extern "C" JNIEXPORT jbyteArray
101+
JNICALL
102+
Java_org_operatorfoundation_audiocoder_CJarInterface_WSPREncodeToFrequencies(JNIEnv *env, jclass cls, jstring j_calls, jstring j_local, jint j_powr, jint j_offset, jboolean lsb_mode) {
103+
// Array to hold the 162 WSPR symbols (0-3 values representing frequency shifts)
104+
uint8_t symbols[WSPR_SYMBOL_COUNT];
105+
106+
// Convert Java strings to C strings
107+
const char *callsign = env->GetStringUTFChars(j_calls, 0);
108+
const char *loca = env->GetStringUTFChars(j_local, 0);
109+
110+
// Format power as 2-digit string (required by encoder)
111+
char powr[3];
112+
snprintf(powr, 3, "%02d", (int) j_powr);
113+
114+
__android_log_print(ANDROID_LOG_INFO,
115+
APPNAME,
116+
"WSPR Frequency Encode: %s %s %s", callsign, loca, powr);
117+
118+
// Encode WSPR message into symbol array
119+
int encode_result = LB_WSPR_Encode2symbolz(symbols, callsign, loca, powr);
120+
__android_log_print(ANDROID_LOG_INFO,
121+
APPNAME,
122+
"WSPR encode result: %d", encode_result);
123+
124+
// Release Java string references
125+
env->ReleaseStringUTFChars(j_calls, callsign);
126+
env->ReleaseStringUTFChars(j_local, loca);
127+
128+
// Allocate array for frequency data (162 frequencies x 8 bytes each)
129+
const int FREQUENCY_ARRAY_SIZE = WSPR_SYMBOL_COUNT * sizeof(int64_t);
130+
int64_t *frequencies = (int64_t *) malloc(FREQUENCY_ARRAY_SIZE);
131+
132+
if (frequencies == NULL)
133+
{
134+
__android_log_print(ANDROID_LOG_ERROR,
135+
APPNAME,
136+
"Failed to allocate frequency array");
137+
return NULL;
138+
}
139+
140+
// Convert each symbol to its corresponding frequency
141+
for (int i = 0; i < WSPR_SYMBOL_COUNT; i++)
142+
{
143+
uint8_t symbol = symbols[i];
144+
145+
// Apply LSB mode inversion if requested
146+
if (lsb_mode)
147+
{
148+
symbol = (uint8_t) (3 - symbol);
149+
}
150+
151+
// Calculate the frequency for this symbol.
152+
// Base frequency: 1500 Hz
153+
// User offset: j_offset Hz
154+
// Symbol spacing: 1.4648 Hz between tones (WSPR standard)
155+
double frequency_hz = 1500.0 + ((double) j_offset) + (symbol * 1.4648);
156+
157+
// Convert to 64-bit signed integer with 0.01 Hz precision (multiply by 100)
158+
frequencies[i] = (int64_t) (frequency_hz * 100.0);
159+
160+
// Debug: Log the first few frequencies
161+
if (i < 5)
162+
{
163+
__android_log_print(ANDROID_LOG_DEBUG,
164+
APPNAME,
165+
"Symbol[%d] = %d, Frequency = %.4f Hz, Encoded = %lld", i, symbol, frequency_hz, (long long)frequencies[i]);
166+
}
167+
}
168+
169+
jbyteArray result = env->NewByteArray(FREQUENCY_ARRAY_SIZE);
170+
if (result == NULL)
171+
{
172+
__android_log_print(ANDROID_LOG_ERROR,
173+
APPNAME,
174+
"Failed to create Java byte array for WSPR encoding.");
175+
free(frequencies);
176+
return NULL;
177+
}
178+
179+
// Copy frequency data to Java byte array
180+
env->SetByteArrayRegion(result, 0, FREQUENCY_ARRAY_SIZE, (jbyte *) frequencies);
181+
182+
// Don't forget to clean up after yourself!
183+
free(frequencies);
184+
185+
__android_log_print(ANDROID_LOG_INFO, APPNAME,
186+
"WSPR frequency encoding complete: %d frequencies, %d bytes",
187+
WSPR_SYMBOL_COUNT, FREQUENCY_ARRAY_SIZE);
188+
189+
return result;
190+
}
191+
192+
83193

84194
extern "C"
85195
JNIEXPORT jint JNICALL

0 commit comments

Comments
 (0)