1% fsk_horus.m 2% David Rowe 10 Oct 2015 3% 4% Project Horus High Altitude Balloon (HAB) FSK demodulator 5% See blog write up "All your modems are belong to us" 6% http://www.rowetel.com/?p=4629 7 8 9fsk_lib; 10 11 12% Basic modem set up for Horus 13 14function states = fsk_horus_init(Fs,Rs,M=2) 15 16 states = fsk_init(Fs,Rs,M); 17 18 % Freq. estimator limits - keep these narrow to stop errors with low SNR 4FSK 19 20 states.fest_fmin = 300; 21 states.fest_fmax = 2800; 22endfunction 23 24 25% init rtty protocol specific states 26 27function rtty = fsk_horus_init_rtty 28 % Generate unque word that correlates against the ASCII "$$$$$" that 29 % is at the start of each frame. 30 % $ -> 36 decimal -> 0 1 0 0 1 0 0 binary 31 32 dollar_bits = fliplr([0 1 0 0 1 0 0]); 33 mapped_db = 2*dollar_bits - 1; 34 sync_bits = [1 1 0]; 35 mapped_sb = 2*sync_bits - 1; 36 37 mapped = [mapped_db mapped_sb]; 38 npad = rtty.npad = 3; % one start and two stop bits between 7 bit ascii chars 39 nfield = rtty.nfield = 7; % length of ascii character field 40 41 rtty.uw = [mapped mapped mapped mapped mapped]; 42 rtty.uw_thresh = length(rtty.uw) - 2; % allow a few bit errors when looking for UW 43 rtty.max_packet_len = 1000; 44endfunction 45 46 47% I think this is the binary protocol work from Jan 2016 48 49function binary = fsk_horus_init_binary 50 % Generate 16 bit "$$" unique word that is at the front of every horus binary 51 % packet 52 53 dollar_bits = [0 0 1 0 0 1 0 0]; 54 mapped_db = 2*dollar_bits - 1; 55 56 binary.uw = [mapped_db mapped_db]; 57 binary.uw_thresh = length(binary.uw)-2; % no bit errors when looking for UW 58 59 binary.max_packet_len = 360; 60endfunction 61 62 63% Look for unique word and return index of first UW bit, or -1 if no 64% UW found Sometimes there may be several matches, returns the 65% position of the best match to UW. 66 67function [uw_start best_corr corr] = find_uw(states, start_bit, rx_bits) 68 uw = states.uw; 69 70 mapped_rx_bits = 2*rx_bits - 1; 71 best_corr = 0; 72 uw_start = -1; 73 found_uw = 0; 74 75 % first first UW in buffer that exceeds threshold 76 77 for i=start_bit:length(rx_bits) - length(uw) 78 corr(i) = mapped_rx_bits(i:i+length(uw)-1) * uw'; 79 if (found_uw == 0) && (corr(i) >= states.uw_thresh) 80 uw_start = i; 81 best_corr = corr; 82 found_uw = 1; 83 end 84 end 85 86endfunction 87 88 89% Extract ASCII string from a Horus frame of bits 90 91function [str crc_ok] = extract_ascii(states, rx_bits_buf, uw_loc) 92 nfield = states.nfield; 93 npad = states.npad; 94 95 str = ""; str_dec = []; nstr = 0; ptx_crc = 1; rx_crc = ""; 96 endpacket = 0; 97 98 st = uw_loc + length(states.uw); % first bit of first char 99 en = uw_loc + states.max_packet_len - nfield; 100 %printf("\nst: %d en: %d len: %d\n", st, en, length(rx_bits_buf)); 101 102 for i=st:nfield+npad:en 103 field = rx_bits_buf(i:i+nfield-1); 104 ch_dec = field * (2.^(0:nfield-1))'; 105 106 % filter out unlikely characters that bit errors may introduce, and ignore \n 107 108 if (ch_dec > 31) && (ch_dec < 91) 109 str = [str char(ch_dec)]; 110 else 111 str = [str char(32)]; % space is "not sure" 112 end 113 nstr++; 114 115 % build up array for CRC16 check 116 117 if !endpacket && (ch_dec == 42) 118 endpacket = 1; 119 rx_crc = crc16(str_dec); % found a '*' so that's the end of the string for CRC calculations 120 ptx_crc = nstr+1; % this is where the transmit CRC starts 121 end 122 if !endpacket 123 str_dec = [str_dec ch_dec]; 124 end 125 end 126 127 if (ptx_crc+3) <= length(str) 128 tx_crc = str(ptx_crc:ptx_crc+3); 129 crc_ok = strcmp(tx_crc, rx_crc); 130 else 131 crc_ok = 0; 132 end 133 134 str = str(1:ptx_crc-2); 135 136endfunction 137 138 139% Use soft decision information to find bits most likely in error. I think 140% this is some form of maximum likelihood decoding. 141 142function [str crc_ok rx_bits_log_flipped] = sd_bit_flipping(states, rx_bits_log, rx_bits_sd_log, st, en); 143 144 % force algorithm to ignore rs232 sync bits by marking them as "very likely", they have 145 % no input to crc algorithm 146 147 nfield = states.nfield; 148 npad = states.npad; 149 for i=st:nfield+npad:en 150 rx_bits_sd_log(i+nfield:i+nfield+npad-1) = 1E6; 151 end 152 153 % make a list of bits with smallest soft decn values 154 155 [dodgy_bits_mag dodgy_bits_index] = sort(abs(rx_bits_sd_log(st+length(states.uw):en))); 156 dodgy_bits_index += length(states.uw) + st - 1; 157 nbits = 6; 158 ntries = 2^nbits; 159 str = ""; 160 crc_ok = 0; 161 162 % try various combinations of these bits 163 164 for i=1:ntries-1 165 error_mask = zeros(1, length(rx_bits_log)); 166 for b=1:nbits 167 x = bitget(i,b); 168 bit_to_flip = dodgy_bits_index(b); 169 error_mask(bit_to_flip) = x; 170 %printf("st: %d i: %d b: %d x: %d index: %d\n", st, i,b,x,bit_to_flip); 171 end 172 rx_bits_log_flipped = xor(rx_bits_log, error_mask); 173 [str_flipped crc_ok_flipped] = extract_ascii(states, rx_bits_log_flipped, st); 174 if crc_ok_flipped 175 %printf("Yayy we fixed a packet by flipping with pattern %d\n", i); 176 str = str_flipped; 177 crc_ok = crc_ok_flipped; 178 end 179 end 180endfunction 181 182 183% Extract as many ASCII packets as we can from a great big buffer of bits 184 185function npackets = extract_and_print_rtty_packets(states, rtty, rx_bits_log, rx_bits_sd_log) 186 187 % use UWs to delimit start and end of data packets 188 189 bit = 1; 190 nbits = length(rx_bits_log); 191 nfield = rtty.nfield; 192 npad = rtty.npad; 193 npackets = 0; 194 195 uw_loc = find_uw(rtty, bit, rx_bits_log, states.verbose); 196 197 while (uw_loc != -1) 198 if bitand(states.verbose,0x8) 199 printf("nbits: %d max_packet_len: %d uw_loc: %d\n", nbits, rtty.max_packet_len, uw_loc); 200 end 201 202 if (uw_loc + rtty.max_packet_len) < nbits 203 % Now start picking out 7 bit ascii chars from frame. It has some 204 % structure so we can guess where fields are. I hope we don't get 205 % RS232 idle bits stuck into it anywhere, ie "bit fields" don't 206 % change dynamically. 207 208 % dump msg bits so we can use them as a test signal 209 %msg = rx_bits_log(st:uw_loc-1); 210 %save -ascii horus_msg.txt msg 211 212 % simulate bit error for testing 213 %rx_bits_log(st+200) = xor(rx_bits_log(st+100),1); 214 %rx_bits_sd_log(st+100) = 0; 215 216 [str crc_ok] = extract_ascii(rtty, rx_bits_log, uw_loc); 217 218 if crc_ok == 0 219 [str_flipped crc_flipped_ok rx_bits_log] = sd_bit_flipping(rtty, rx_bits_log, rx_bits_sd_log, uw_loc, uw_loc+rtty.max_packet_len); 220 end 221 222 % update memory of previous packet, we use this to guess where errors may be 223 if crc_ok || crc_flipped_ok 224 states.prev_pkt = rx_bits_log(uw_loc+length(rtty.uw):uw_loc+rtty.max_packet_len); 225 end 226 227 if crc_ok 228 str = sprintf("%s CRC OK", str); 229 npackets++; 230 else 231 if crc_flipped_ok 232 str = sprintf("%s fixed", str_flipped); 233 else 234 str = sprintf("%s CRC BAD", str); 235 end 236 end 237 printf("%s\n", str); 238 end 239 240 % look for next packet 241 242 bit = uw_loc + length(rtty.uw); 243 uw_loc = find_uw(rtty, bit, rx_bits_log, states.verbose); 244 245 endwhile 246endfunction 247 248 249% Extract as many binary packets as we can from a great big buffer of bits, 250% and send them to the C decoder for FEC decoding. 251% horus_l2 can be compiled a bunch of different ways. You need to 252% compile with: 253% codec2-dev/src$ gcc horus_l2.c -o horus_l2 -Wall -DDEC_RX_BITS -DHORUS_L2_RX 254 255function corr_log = extract_and_decode_binary_packets(states, binary, rx_bits_log) 256 corr_log = []; 257 258 % use UWs to delimit start and end of data packets 259 260 bit = 1; 261 nbits = length(rx_bits_log); 262 263 [uw_loc best_corr corr] = find_uw(binary, bit, rx_bits_log, states.verbose); 264 corr_log = [corr_log corr]; 265 266 while (uw_loc != -1) 267 268 if (uw_loc+binary.max_packet_len) < nbits 269 % printf("uw_loc: %d best_corr: %d\n", uw_loc, best_corr); 270 271 % OK we have a packet delimited by two UWs. Lets convert the bit 272 % stream into bytes and save for decoding 273 274 pin = uw_loc; 275 for i=1:45 276 rx_bytes(i) = rx_bits_log(pin:pin+7) * (2.^(7:-1:0))'; 277 pin += 8; 278 %printf("%d 0x%02x\n", i, rx_bytes(i)); 279 end 280 281 f=fopen("horus_rx_bits_binary.bin","wb"); 282 fwrite(f, rx_bytes, "uchar"); 283 fclose(f); 284 285 % optionally write packet to disk to use as horus_tx_bits_binary.txt 286 f=fopen("horus_rx_bits_binary.txt","wt"); 287 for i=uw_loc:uw_loc+45*8-1 288 fprintf(f, "%d ", rx_bits_log(i)); 289 end 290 fclose(f); 291 292 system("../src/horus_l2"); % compile instructions above 293 end 294 295 bit = uw_loc + length(binary.uw); 296 [uw_loc best_corr corr] = find_uw(binary, bit, rx_bits_log, states.verbose); 297 corr_log = [corr_log corr]; 298 299 endwhile 300 301endfunction 302 303 304% simulation of tx and rx side, add noise, channel impairments ---------------------- 305% 306% test_frame_mode Description 307% 1 BER testing using known test frames 308% 2 random bits 309% 3 repeating sequence of all symbols 310% 4 Horus RTTY 311% 5 Horus Binary 312% 6 Horus High Speed: A 8x oversampled modem, e.g. Fs=9600, Rs=1200 313% which is the same as Fs=921600 Rs=115200 314% Uses packet based BER counter 315 316function run_sim(test_frame_mode, M=2, frames = 10, EbNodB = 100, filename="fsk_horus.raw") 317 timing_offset = 0.0; % see resample() for clock offset below 318 fading = 0; % modulates tx power at 2Hz with 20dB fade depth, 319 % to simulate balloon rotating at end of mission 320 df = 0; % tx tone freq drift in Hz/s 321 dA = 1; % amplitude imbalance of tones (note this affects Eb so not a gd idea) 322 323 more off 324 rand('state',1); 325 randn('state',1); 326 327 % ---------------------------------------------------------------------- 328 329 % sm2000 config ------------------------ 330 %states = fsk_horus_init(96000, 1200); 331 %states.f1_tx = 4000; 332 %states.f2_tx = 5200; 333 334 if test_frame_mode < 4 335 % horus rtty config --------------------- 336 states = fsk_horus_init(8000, 50, M); 337 end 338 339 if test_frame_mode == 4 340 % horus rtty config --------------------- 341 states = fsk_horus_init(8000, 100, 2); 342 states.tx_bits_file = "horus_payload_rtty.txt"; % Octave file of bits we FSK modulate 343 rtty = fsk_horus_init_rtty; 344 states.ntestframebits = rtty.max_packet_len; 345 end 346 347 if test_frame_mode == 5 348 % horus binary config --------------------- 349 states = fsk_horus_init(8000, 100, 4); 350 binary = fsk_horus_init_binary; 351 states.tx_bits_file = "horus_tx_bits_binary.txt"; % Octave file of bits we FSK modulate 352 states.ntestframebits = binary.max_packet_len; 353 end 354 355 if test_frame_mode == 6 356 % horus high speed --------------------- 357 states = fsk_horus_init(Fs=9600, Rs=1200, M=2, P=8, nsym=16); 358 states.tx_bits_file = "horus_high_speed.bin"; 359 end 360 361 % Tones must be at least Rs apart for ideal non-coherent FSK 362 363 states.ftx = 900 + 2*states.Rs*(1:states.M); 364 states.tx_tone_separation = states.ftx(2) - states.ftx(1); 365 366 % ---------------------------------------------------------------------- 367 368 states.verbose = 0x1; 369 M = states.M; 370 N = states.N; 371 P = states.P; 372 Rs = states.Rs; 373 nsym = states.nsym; 374 nbit = states.nbit; 375 Fs = states.Fs; 376 states.df(1:M) = df; 377 states.dA(1:M) = dA; 378 379 % optional noise. Useful for testing performance of waveforms from real world modulators 380 381 EbNo = 10^(EbNodB/10); 382 variance = states.Fs/(states.Rs*EbNo*states.bitspersymbol); 383 384 % set up tx signal with payload bits based on test mode 385 386 if (test_frame_mode == 1) 387 % test frame of bits, which we repeat for convenience when BER testing 388 states.ntestframebits = states.nbit; 389 test_frame = round(rand(1, states.ntestframebits)); 390 tx_bits = []; 391 for i=1:frames+1 392 tx_bits = [tx_bits test_frame]; 393 end 394 end 395 396 if test_frame_mode == 2 397 % random bits, just to make sure sync algs work on random data 398 tx_bits = round(rand(1, states.nbit*(frames+1))); 399 end 400 401 if test_frame_mode == 3 402 % repeating sequence of all symbols 403 % great for initial test of demod if nothing else works, 404 % look for this pattern in rx_bits 405 if M == 2 406 % ...10101... 407 tx_bits = zeros(1, states.nbit*(frames+1)); 408 tx_bits(1:2:length(tx_bits)) = 1; 409 else 410 % repeat each possible 4fsk symbol 411 pattern = [0 0 0 1 1 0 1 1]; 412 %pattern = [0 0 0 1 1 1 1 0]; 413 nrepeats = states.nbit*(frames+1)/length(pattern); 414 tx_bits = []; 415 for b=1:nrepeats 416 tx_bits = [tx_bits pattern]; 417 end 418 %tx_bits = zeros(1, states.nbit*(frames+1)); 419 end 420 end 421 422 if (test_frame_mode == 4) || (test_frame_mode == 5) 423 424 % load up a horus msg from disk and modulate that 425 426 test_frame = load(states.tx_bits_file); 427 ltf = length(test_frame); 428 ntest_frames = ceil((frames+1)*nbit/ltf); 429 printf("Generating %d test packets\n", ntest_frames); 430 431 % 1 second of random bits to let estimators lock on 432 433 preamble = round(rand(1,states.Rs)); 434 435 tx_bits = preamble; 436 for i=1:ntest_frames 437 tx_bits = [tx_bits test_frame]; 438 end 439 440 % a packet len of random bits at end fill buffers to deocode final packet 441 442 if test_frame_mode == 4 443 postamble = round(rand(1,rtty.max_packet_len)); 444 else 445 postamble = round(rand(1,binary.max_packet_len)); 446 end 447 tx_bits = [tx_bits postamble]; 448 end 449 450 if test_frame_mode == 6 451 states.verbose += 0x4; 452 ftmp = fopen(states.tx_bits_file, "rb"); test_frame = fread(ftmp,Inf,"char")'; fclose(ftmp); 453 states.ntestframebits = length(test_frame); 454 printf("length test frame: %d\n", states.ntestframebits); 455 %test_frame = rand(1,states.ntestframebits) > 0.5; 456 457 tx_bits = []; 458 for i=1:frames+1 459 tx_bits = [tx_bits test_frame]; 460 end 461 end 462 463 tx = fsk_mod(states, tx_bits); 464 465 %tx = resample(tx, 1000, 1001); % simulated 1000ppm sample clock offset 466 467 if fading 468 ltx = length(tx); 469 tx = tx .* (1.1 + cos(2*pi*2*(0:ltx-1)/Fs))'; % min amplitude 0.1, -20dB fade, max 3dB 470 end 471 472 noise = sqrt(variance)*randn(length(tx),1); 473 rx = tx + noise; 474 printf("SNRdB meas: %4.1f\n", 10*log10(var(tx)/var(noise))); 475 476 % dump simulated rx file 477 478 ftx=fopen(filename,"wb"); rxg = rx*1000; fwrite(ftx, rxg, "short"); fclose(ftx); 479 480 timing_offset_samples = round(timing_offset*states.Ts); 481 st = 1 + timing_offset_samples; 482 rx_bits_buf = zeros(1,nbit+states.ntestframebits); 483 x_log = []; 484 timing_nl_log = []; 485 norm_rx_timing_log = []; 486 f_int_resample_log = []; 487 f_log = []; 488 EbNodB_log = []; 489 rx_bits_log = []; 490 rx_bits_sd_log = []; 491 492 % main loop --------------------------------------------------------------- 493 494 run_frames = floor(length(rx)/N)-1; 495 for f=1:run_frames 496 497 % extract nin samples from input stream 498 499 nin = states.nin; 500 en = st + states.nin - 1; 501 502 if en < length(rx) % due to nin variations its possible to overrun buffer 503 sf = rx(st:en); 504 st += nin; 505 506 % demodulate to stream of bits 507 508 states = est_freq(states, sf, states.M); 509 %states.f = 900 + 2*states.Rs*(1:states.M); 510 %states.f = [1200 1400 1600 1800]; 511 [rx_bits states] = fsk_demod(states, sf); 512 513 rx_bits_buf(1:states.ntestframebits) = rx_bits_buf(nbit+1:states.ntestframebits+nbit); 514 rx_bits_buf(states.ntestframebits+1:states.ntestframebits+nbit) = rx_bits; 515 %rx_bits_buf(1:nbit) = rx_bits_buf(nbit+1:2*nbit); 516 %rx_bits_buf(nbit+1:2*nbit) = rx_bits; 517 518 rx_bits_log = [rx_bits_log rx_bits]; 519 rx_bits_sd_log = [rx_bits_sd_log states.rx_bits_sd]; 520 521 norm_rx_timing_log = [norm_rx_timing_log states.norm_rx_timing]; 522 x_log = [x_log states.x]; 523 timing_nl_log = [timing_nl_log states.timing_nl]; 524 f_int_resample_log = [f_int_resample_log abs(states.f_int_resample(:,:))]; 525 f_log = [f_log; states.f]; 526 EbNodB_log = [EbNodB_log states.EbNodB]; 527 528 if test_frame_mode == 1 529 states = ber_counter(states, test_frame, rx_bits_buf); 530 end 531 if test_frame_mode == 6 532 states = ber_counter_packet(states, test_frame, rx_bits_buf); 533 end 534 end 535 end 536 537 % print stats, count errors, decode packets ------------------------------------------ 538 539 if (test_frame_mode == 1) || (test_frame_mode == 6) 540 printf("frames: %d EbNo: %3.2f Tbits: %d Terrs: %d BER %4.3f\n", frames, EbNodB, states.Tbits,states. Terrs, states.Terrs/states.Tbits); 541 end 542 543 if test_frame_mode == 4 544 npackets = extract_and_print_rtty_packets(states, rtty, rx_bits_log, rx_bits_sd_log); 545 printf("Received %d packets\n", npackets); 546 end 547 548 if test_frame_mode == 5 549 extract_and_decode_binary_packets(states, binary, rx_bits_log); 550 end 551 552 figure(1); 553 plot(f_int_resample_log','+') 554 hold off; 555 556 figure(2) 557 clf 558 m = max(abs(x_log)); 559 plot(x_log,'+') 560 axis([-m m -m m]) 561 title('fine timing metric') 562 563 figure(3) 564 clf 565 subplot(211) 566 plot(norm_rx_timing_log); 567 axis([1 run_frames -1 1]) 568 title('norm fine timing') 569 subplot(212) 570 plot(states.nerr_log) 571 title('num bit errors each frame') 572 573 figure(4) 574 clf 575 subplot(211) 576 one_sec_rx = rx(1:min(Fs,length(rx))); 577 plot(one_sec_rx) 578 title('rx signal at demod input') 579 subplot(212) 580 plot(abs(fft(one_sec_rx))) 581 582 figure(5) 583 clf 584 plot(f_log,'+') 585 title('tone frequencies') 586 axis([1 run_frames 0 Fs/2]) 587 588 figure(6) 589 clf 590 plot(EbNodB_log); 591 title('Eb/No estimate') 592 593 figure(7) 594 clf 595 subplot(211) 596 X = abs(fft(timing_nl_log)); 597 plot(X(1:length(X)/2)) 598 subplot(212) 599 plot(abs(timing_nl_log(1:100))) 600 601 endfunction 602 603 604% --------------------------------------------------------------------- 605% demodulate from a user-supplied file 606% --------------------------------------------------------------------- 607 608function rx_bits_log = demod_file(filename, test_frame_mode=4, noplot=0, EbNodB=100, max_frames=1E32) 609 fin = fopen(filename,"rb"); 610 more off; 611 read_complex = 0; sample_size = 'int16'; shift_fs_on_4 = 0; 612 max_frames 613 614 if test_frame_mode == 4 615 % horus rtty config --------------------- 616 states = fsk_horus_init(8000, 100, 2); 617 rtty = fsk_horus_init_rtty; 618 states.ntestframebits = rtty.max_packet_len; 619 end 620 621 if test_frame_mode == 5 622 % horus binary config --------------------- 623 states = fsk_horus_init(8000, 100, 4); 624 binary = fsk_horus_init_binary; 625 states.ntestframebits = binary.max_packet_len; 626 end 627 628 states.verbose = 0x1 + 0x8; 629 630 if test_frame_mode == 6 631 % Horus high speed config -------------- 632 states = fsk_horus_init(Fs=9600, Rs=1200, M=2, P=8, nsym=16); 633 states.tx_bits_file = "horus_high_speed.bin"; 634 states.verbose += 0x4; 635 ftmp = fopen(states.tx_bits_file, "rb"); test_frame = fread(ftmp,Inf,"char")'; fclose(ftmp); 636 states.ntestframebits = length(test_frame); 637 printf("length test frame: %d\n", states.ntestframebits); 638 end 639 640 if test_frame_mode == 7 641 % 800XA 4FSK modem -------------- 642 states = fsk_init(Fs=8000, Rs=400, M=4, P=10, nsym=256); 643 states.tx_bits_file = "horus_high_speed.bin"; 644 states.verbose += 0x4; 645 ftmp = fopen(states.tx_bits_file, "rb"); test_frame = fread(ftmp,Inf,"char")'; fclose(ftmp); 646 states.ntestframebits = length(test_frame); 647 printf("length test frame: %d\n", states.ntestframebits); 648 end 649 650 if test_frame_mode == 8 651 % test RS41 type balllon telemetry -------------- 652 states = fsk_init(96000, 4800, 2, 10, 16); 653 states.fest_fmin = 1000; 654 states.fest_fmax = 40000; 655 states.fest_min_spacing = 1000; 656 states.tx_bits_file = "../build_linux/src/tx_bit.bin"; 657 states.verbose += 0x4; 658 #ftmp = fopen(states.tx_bits_file, "rb"); test_frame = fread(ftmp,Inf,"char")'; fclose(ftmp); 659 #states.ntestframebits = length(test_frame); 660 #printf("length test frame: %d\n", states.ntestframebits); 661 states.ntestframebits = 1000; 662 read_complex = 1; 663 shift_fs_on_4 = 1; % get samples into range of current freq estimator 664 end 665 666 if test_frame_mode == 9 667 % Wenet high speed SSTV, we can just check raw demo here --------------------- 668 % despite the high sample rate the modem sees this as a 8:1 Fs/Rs configuration 669 states = fsk_init(8000, 1000, 2); 670 states.tx_tone_separation = 1000; 671 states.ntestframebits = (256+2+65)*8+40; % from src/drs232_lpc.c 672 states.freq_est_type = 'mask'; 673 read_complex=1; sample_size = 'uint8'; 674 printf("Wenet mode: ntestframebits: %d freq_est_type: %s\n", states.ntestframebits, states.freq_est_type); 675 %states.verbose = 0x8; 676 end 677 678 N = states.N; 679 P = states.P; 680 Rs = states.Rs; 681 nsym = states.nsym; 682 nbit = states.nbit; 683 684 frames = 0; 685 rx = []; 686 rx_bits_log = []; 687 rx_bits_sd_log = []; 688 norm_rx_timing_log = []; 689 f_int_resample_log = []; 690 EbNodB_log = []; 691 ppm_log = []; 692 f_log = []; Sf_log = []; 693 694 rx_bits_buf = zeros(1,nbit + states.ntestframebits); 695 696 % optional noise. Useful for testing performance of waveforms from real world modulators 697 % we need to pre-read the file to estimate the signal power 698 ftmp = fopen(filename,"rb"); s = fread(ftmp,Inf,sample_size); fclose(ftmp); 699 if sample_size == "uint8" s = (s - 127)/128; end 700 if read_complex s = s(1:2:end) + j*s(2:2:end); end 701 tx_pwr = var(s); 702 EbNo = 10^(EbNodB/10); 703 variance = (tx_pwr/2)*states.Fs/(states.Rs*EbNo*states.bitspersymbol); 704 705 % First extract raw bits from samples ------------------------------------------------------ 706 707 printf("demod of raw bits....\n"); 708 709 finished = 0; ph = 1; 710 while (finished == 0) 711 712 % extract nin samples from input stream 713 714 nin = states.nin; 715 if read_complex 716 [sf count] = fread(fin, 2*nin, sample_size); 717 if sample_size == "uint8" sf = (sf - 127)/128; end 718 sf = sf(1:2:end) + j*sf(2:2:end); 719 count /= 2; 720 if shift_fs_on_4 721 % optional shift up in freq by Fs/4 to get into freq est range 722 for i=1:count 723 ph = ph*exp(j*pi/4); 724 sf(i) *= ph; 725 end 726 end 727 else 728 [sf count] = fread(fin, nin, "short"); 729 end 730 rx = [rx; sf]; 731 732 % add optional noise 733 734 if count 735 noise = sqrt(variance)*randn(count,1); 736 sf += noise; 737 end 738 739 if count == nin 740 frames++; 741 742 % demodulate to stream of bits 743 744 states = est_freq(states, sf, states.M); 745 if states.freq_est_type == 'mask' states.f = states.f2; end 746 [rx_bits states] = fsk_demod(states, sf); 747 748 rx_bits_buf(1:states.ntestframebits) = rx_bits_buf(nbit+1:states.ntestframebits+nbit); 749 rx_bits_buf(states.ntestframebits+1:states.ntestframebits+nbit) = rx_bits; 750 751 rx_bits_log = [rx_bits_log rx_bits]; 752 rx_bits_sd_log = [rx_bits_sd_log states.rx_bits_sd]; 753 norm_rx_timing_log = [norm_rx_timing_log states.norm_rx_timing]; 754 f_int_resample_log = [f_int_resample_log abs(states.f_int_resample)]; 755 EbNodB_log = [EbNodB_log states.EbNodB]; 756 ppm_log = [ppm_log states.ppm]; 757 f_log = [f_log; states.f]; 758 Sf_log = [Sf_log; states.Sf']; 759 760 if (test_frame_mode == 1) 761 states = ber_counter(states, test_frame, rx_bits_buf); 762 if states.ber_state == 1 763 states.verbose = 0; 764 end 765 end 766 if (test_frame_mode == 6) % || (test_frame_mode == 8) 767 states = ber_counter_packet(states, test_frame, rx_bits_buf); 768 end 769 else 770 finished = 1; 771 end 772 773 if frames > max_frames finished=1; end 774 775 end 776 printf("frames: %d\n", frames); 777 fclose(fin); 778 779 if noplot == 0 780 printf("plotting...\n"); 781 782 figure(1); clf; 783 plot(f_log); 784 title('Tone Freq Estimates'); 785 786 figure(2); 787 plot(f_int_resample_log','+') 788 title('Integrator outputs for each tone'); 789 790 figure(3); clf 791 subplot(211) 792 plot(norm_rx_timing_log) 793 axis([1 frames -0.5 0.5]) 794 title('norm fine timing') 795 subplot(212) 796 plot(states.nerr_log) 797 title('num bit errors each frame') 798 799 figure(4); clf 800 plot(EbNodB_log); 801 title('Eb/No estimate') 802 803 figure(5); clf 804 rx_nowave = rx(1000:length(rx)); % skip past wav header if it's a wave file 805 subplot(211) 806 plot(real(rx_nowave)); 807 title('input signal to demod (1 sec)') 808 xlabel('Time (samples)'); 809 %axis([1 states.Fs -35000 35000]) 810 811 % normalise spectrum to 0dB full scale with sine wave input 812 subplot(212); 813 if sample_size == "int16" max_value = 32767; end 814 if sample_size == "uint8" max_value = 127; end 815 RxdBFS = 20*log10(abs(fft(rx_nowave(1:states.Fs)))) - 20*log10((states.Fs/2)*max_value); 816 plot(RxdBFS) 817 axis([1 states.Fs/2 -80 0]) 818 xlabel('Frequency (Hz)'); 819 820 figure(6); clf 821 plot(ppm_log) 822 title('Sample clock (baud rate) offset in PPM'); 823 824 figure(7); clf; mesh(Sf_log(1:10,:)); 825 end 826 827 if (test_frame_mode == 1) || (test_frame_mode == 6) 828 printf("frames: %d Tbits: %d Terrs: %d BER %4.3f EbNo: %3.2f\n", frames, states.Tbits,states. Terrs, states.Terrs/states.Tbits, mean(EbNodB_log)); 829 end 830 831 % we can decode both protocols at the same time 832 833 if (test_frame_mode == 4) 834 npackets = extract_and_print_rtty_packets(states, rtty, rx_bits_log, rx_bits_sd_log) 835 printf("Received %d packets\n", npackets); 836 end 837 838 if (test_frame_mode == 5) 839 corr_log = extract_and_decode_binary_packets(states, binary, rx_bits_log); 840 841 figure(8); 842 clf 843 plot(corr_log); 844 hold on; 845 plot([1 length(corr_log)],[binary.uw_thresh binary.uw_thresh],'g'); 846 hold off; 847 title('UW correlation'); 848 end 849 850endfunction 851 852 853% Over the years this modem has been used for many different FSK signals ... 854 855if exist("fsk_horus_as_a_lib") == 0 856 run_sim(test_frame_mode=4, M=2, frames=30, EbNodB = 20); 857 %run_sim(5, 4, 30, 100); 858 %rx_bits = demod_file("~/Desktop/115.wav",6,0,90); 859 %rx_bits = demod_file("~/Desktop/fsk_800xa_rx_hackrf.wav",7); 860 %rx_bits = demod_file("~/Desktop/2fsk_100_rx_rpi_rtlsdr_002_ledger.wav",4); 861 %rx_bits = demod_file("~/Desktop/phorus_binary_ascii.wav",4); 862 %rx_bits = demod_file("~/Desktop/binary/horus_160102_binary_rtty_2.wav",4); 863 %rx_bits = demod_file("~/Desktop/horus_160102_vk5ei_capture2.wav",4); 864 %rx_bits = demod_file("~/Desktop/horus_rtty_binary.wav",4); 865 %rx_bits = demod_file("~/Desktop/FSK_4FSK.wav",4); 866 %rx_bits = demod_file("t.raw",5); 867 %rx_bits = demod_file("~/Desktop/fsk_horus_10dB_1000ppm.wav",4); 868 %rx_bits = demod_file("~/Desktop/fsk_horus_6dB_0ppm.wav",4); 869 %rx_bits = demod_file("test.raw",1,1); 870 %rx_bits = .rawdemod_file("/dev/ttyACM0",1); 871 %rx_bits = demod_file("fsk_horus_rx_1200_96k.raw",1); 872 %rx_bits = demod_file("mp.raw",4); 873 %rx_bits = demod_file("~/Desktop/launchbox_v2_landing_8KHz_final.wav",4); 874 %rx_bits = demod_file("~/Desktop/fsk_800xa.wav",7); 875 %rx_bits = demod_file("~/Desktop/rs41_96k_10s.iq16",8); 876end 877