Projet: Réaliser une station de décodage FT8 sans affichage graphique pour un serveur. Un deuxième script pour l’envoyer sur PSKREPORTER
1.Installation des outils nécessaires:
Voir l’article sur la modification de arecord pour 384khz si nécéssaire.
Installer wsjtx avec sudo apt-get install wsjtx
2.Le script de decodage FT8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#!/bin/bash function usage(){ printf "Utilisation du script :\n" printf "$0 alsa_device centerfreq freq band samplerate\n" printf "Example :\n" printf "$0 dsnoop:lp4,1 3568600 3568600 80m 384000 F4HTB JN38tg\n" } if [ $# -ne 7 ] then usage exit 1 fi alsa_device=$1 centerfreq=$2 freq=$3 band=$4 samplerate=$5 call=$6 grid=$7 decimationfactor=$(($samplerate/12000)) pathft8=/dev/shm/FT8_$band rm -rf $pathft8 >/dev/null 2>&1 mkdir -p $pathft8 >/dev/null 2>&1 echo "Wait 0s to start" wait_0 () { while [[ ("$(( $(date +"%_S") % 15))" -ne 0 ) ]] do sleep 0.1 done } if [ $freq -eq $centerfreq ] then while true do wait_0 now=$(date +"%y%m%d_%H%M%S") echo "Start for $now on $band" arecord -q -d 14 -f S16_LE -r $samplerate -c2 -D $alsa_device -F0 --period-size=1024 -B0 --buffer-size=4096 | csdr convert_s16_f | csdr fir_decimate_cc $decimationfactor 0.05 HAMMING 2>/dev/null | csdr bandpass_fir_fft_cc -0.5 0 0.05 2>/dev/null | csdr realpart_cf | csdr agc_ff | csdr convert_f_s16 | sox -q -t raw -b 16 -e signed -c 1 -r 12000 - -t wav $pathft8/$now.wav jt9 -e $pathft8 -a $pathft8 -t $pathft8 --ft8 -d 3 $pathft8/$now.wav | grep -v "DecodeFinished" | grep -v "EOF" | awk -v freqon=$freq '{gsub("~",freqon,$5);print $0}' | awk -v pathft8on=$pathft8 '{print >> pathft8on"/spotstosend.txt"};{print};' >>spots_$band && rm -rf $pathft8/$now.wav & if [[ ("$(( $(date +"%_M") % 6))" -eq 0 ) && ("$(( $(date +"%_S")))" -lt 20 ) ]]; then echo "Send to pskreporter for $now on $band" ./uploadpskreporter.pl $call $grid $pathft8/spotstosend.txt & fi done fi if [ $freq -ne $centerfreq ] then while true do wait_0 now=$(date +"%y%m%d_%H%M%S") echo "Start for $now on $band" arecord -q -d 14 -f S16_LE -r $samplerate -c2 -D $alsa_device -F0 --period-size=1024 -B0 --buffer-size=4096 | csdr convert_s16_f | csdr shift_addition_cc `python3 -c "print(float($freq-$centerfreq)/$samplerate)"` 2>/dev/null | csdr fir_decimate_cc $decimationfactor 0.05 HAMMING 2>/dev/null | csdr bandpass_fir_fft_cc -0.5 0 0.05 2>/dev/null | csdr realpart_cf | csdr agc_ff | csdr convert_f_s16 | sox -q -t raw -b 16 -e signed -c 1 -r 12000 - -t wav $pathft8/$now.wav jt9 -e $pathft8 -a $pathft8 -t $pathft8 --ft8 -d 3 $pathft8/$now.wav | grep -v "DecodeFinished" | grep -v "EOF" | awk -v freqon=$freq '{gsub("~",freqon,$5);print $0}' | awk -v pathft8on=$pathft8 '{print >> pathft8on"/spotstosend.txt"};{print};' >>spots_$band && rm -rf $pathft8/$now.wav & if [[ ("$(( $(date +"%_M") % 6))" -eq 0 ) && ("$(( $(date +"%_S")))" -lt 20 ) ]]; then echo "Send to pskreporter for $now on $band" ./uploadpskreporter.pl $call $grid $pathft8/spotstosend.txt & fi done fi |
3.Le script d’upload vers pskreporter pour FT8 uniquement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
#!/usr/bin/perl # For KiwiSDR # Gather decodes from FT8 log file /dev/shm/decode-ft8.log file of format # 163144 4 -0.9 2431 14074 KK5ZV VE5TLW R-16 # and create PSKReporter UDP datagram for upload per developer info # https://www.pskreporter.info/pskdev.html # Process all messages to include # CQ CALL1 GRID, CALL1 CALL2 GRID, CALL1 CALL2 RPT, CALL1 CALL2 RR73, etc. # cache call signs up to 5 minutes before resending (see $MINTIME) # v0.2.7 - 2018/11/16 - K1RA@K1RA.us # Start by using following command line # ./pskr.pl YOURCALL YOURGRID # ./pskr.pl WX1YZ AB12DE use strict; use warnings; use Time::Piece; use POSIX qw(strftime); use IO::Socket; # minimum number of minutes to wait before sending datagrams my $MINTIME = 5; # maximum UDP IPFIX datagram size in bytes minus header descriptor length my $MAXDATA = 1250; # PSKReporter upload address and port my $peerhost = "report.pskreporter.info"; my $peerport = 4739; # Software Descriptor for PSKReporter my $decsw = "FT8-Skimmer v0.2.7"; # check for YOUR CALL SIGN if( ! defined( $ARGV[0]) || ( ! ( $ARGV[0] =~ /\w\d+\w/)) ) { die "Enter a valid call sign\n"; } my $mycall = uc( $ARGV[0]); # check for YOUR GRID SQUARE (6 digit) if( ! defined( $ARGV[1]) || ( ! ( $ARGV[1] =~ /\w\w\d\d\w\w/)) ) { die "Enter a valid 6 digit grid\n"; } my $mygrid = uc( $ARGV[1]); if( (! defined( $ARGV[2])) || (! -e $ARGV[2] ) ) { die "Enter a valid spots log file\n"; } my $logfile = $ARGV[2]; # IPFIX header # 00 0A ll ll tt tt tt tt ss ss ss ss ii ii ii ii # 'll ll' packet length, 'tt tt tt tt' UNIX time secs, 'ss ss ss ss' sequence number, # 'ii ii ii ii' random id my $header = "00 0a "; # pack header into byte stream $header = join( "", split(" ", $header)); # generate a unique ID - ii ii ii ii my $rid = pack( "n", int( rand( 65535))) . pack( "n", int( rand( 65535))); # Record Format Descriptors # Receiver Info - receiverCallsign, receiverLocator, decodingSoftware my $rcvr = "00 03 00 24 99 92 00 03 00 00 " . "80 02 FF FF 00 00 76 8F " . "80 04 FF FF 00 00 76 8F " . "80 08 FF FF 00 00 76 8F " . "00 00"; # pack above into byte stream $rcvr = pack( "H*", join( "", split(" ", $rcvr))); # Sender Info - senderCallsign, frequency, sNR (1 byte), mode (1 byte), informationSource, senderLocator, flowStartSeconds my $sndr = "00 02 00 3C 99 93 00 07 " . "80 01 FF FF 00 00 76 8F " . "80 05 00 04 00 00 76 8F " . "80 06 00 01 00 00 76 8F " . "80 0A FF FF 00 00 76 8F " . "80 0B 00 01 00 00 76 8F " . "80 03 FF FF 00 00 76 8F " . "00 96 00 04"; # pack above into byte stream $sndr = pack( "H*", join( "", split(" ", $sndr))); # Receiver data header my $rxhdr = "99 92 "; # pack rxhdr into byte stream $rxhdr = join( "", split(" ", $rxhdr)); # Receiver data record my $rxrcd = ( pack( "c", length( $mycall)) . pack( "A*", $mycall) . pack( "c", length( $mygrid)) . pack( "A*", $mygrid) . pack( "c", length( $decsw)) . pack( "A*", $decsw) ); # Determine record length and calculate padding to multiple of 4 bytes my $rxrcdlen = length( $rxrcd)+ 4; my $rxrcdpad = 4 - ( $rxrcdlen % 4); # pack header, data record and padding into complete record $rxrcd = ( pack( "H*", $rxhdr) . pack( "n", ( $rxrcdlen + $rxrcdpad)) . $rxrcd . pack( "x$rxrcdpad") ); # Sender data header my $txhdr = "99 93 "; # pack txhdr into byte stream $txhdr = join( "", split(" ", $txhdr)); # Sender record, length and padding my $txrcd; my $txrcdlen; my $txrcdpad; # FT8 fields from FT8 decoder log file my $gmt; my $x; my $snr; my $dt; my $tone; my $freq; my @rest; my $ft8msg; my $time; my $call; my $grid; # holds one single log file line my $line; # we're only supporting FT8 my $mode = "FT8"; # outgoing UDP datagram packet to be sent to PSKReporter my $packet; # Running sequence number for datagrams my $seq = 1; # hash of deduplicated unique calls per band (key is call + band) my %db; # call+band key for %db hash my $cb; # minute counter to buffer decode lines my $min = 0; # unique band key for hash array above my $base; # decode counter my $d = 0; # datagram size in bytes my $ds; $| = 1; if (open(my $LOG, '<:encoding(UTF-8)', $logfile)) { while (my $line = <$LOG>) { chomp $line; # check if this is a valid FT8 decode line beginning with 6 digit time stamp # check if this is a valid FT8 decode line beginning with 6 digit time stamp if( ( $line =~ /^\d{6}\s/) ) { # no - go to read next line from decoder log # 163144 4 -0.9 2431 14074 KK5ZV VE5TLW R-16 # looks like a valid line split into variable fields ($gmt, $snr, $dt, $tone, $freq, @rest)= split( " ", $line); $freq = $freq + $tone; # get UNIX time since epoch #$time = time(); my $datestring = strftime "%Y%m%d", gmtime; my $t = Time::Piece->strptime($datestring.$gmt, "%Y%m%d%H%M%S"); $time = $t->epoch; # determine base frequency key for hash lookup into FT8 base band frequency array $base = int( $freq / 10000000); # make freq an integer $freq += 0; # make the FT8 message by appending remainder of line into one variable, space delimited $ft8msg = join( " ", @rest); # Here are all the various FT8 message scenarios we will recognize, extract senders CALL & GRID # CQ CALL LLnn if( $ft8msg =~ /^CQ\s([\w\d\/]{3,})\s(\w\w\d\d)/) { $call = $1; $grid = $2; # CQ [NA,DX,xx] CALL LLnn } elsif ( $ft8msg =~ /^CQ\s\w{2}\s([\w\d\/]{3,})\s(\w\w\d\d)/) { $call = $1; $grid = $2; # CALL1 CALL2 [R][-+]nn } elsif ( $ft8msg =~ /^[\w\d\/]{3,}\s([\w\d\/]{3,})\sR*[\-+][0-9]{2}/) { $call = $1; $grid = ""; # CALL1 CALL2 RRR } elsif ( $ft8msg =~ /^[\w\d\/]{3,}\s([\w\d\/]{3,})\sRRR/) { $call = $1; $grid = ""; # CALL1 CALL2 RR73 or 73 } elsif ( $ft8msg =~ /^[\w\d\/]{3,}\s([\w\d\/]{3,})\sR*73/) { $call = $1; $grid = ""; # CALL1 CALL2 GRID } elsif ( $ft8msg =~ /^[\w\d\/]{3,}\s([\w\d\/]{3,})\s(\w\w\d\d)/) { $call = $1; $grid = $2; } else { next; } # does the call have at least one number in it if( ! ( $call =~ /\d/) ) { # no - maybe be this is a TNX, NAME, QSL message, so skip this line next; } # have we NOT seen this call on this band yet if( ! defined( $db{$call.$base}) ) { # yes - save it to hash array $db{$call.$base} = $time.",".$call.",".$grid.",".$freq.",".$snr; # keep count of unique FT8 call+base decodes in hash array for this time window $d++; } else { # no - we have seen before, so did we get a grid this decode if( $grid ne "") { # yes - resave decode with grid just in case we didn't before $db{$call.$base} = $time.",".$call.",".$grid.",".$freq.",".$snr; } } } } } else { warn "Could not open file '$logfile' $!"; } # loop until all decodes in hash array are packed and sent in datagrams while( $d > 0) { undef $packet; undef $txrcd; # loop thru all call+base keys and pack buffered decodes into datagram foreach $cb (sort ( keys %db)) { # split hash into individual variable fields ( $time, $call, $grid, $freq, $snr) = split( ",", $db{$cb} ); #my $t = Time::Piece->strptime( $time, '%s' ); #print $time ." ". $t->strftime ." ". $call ."\n"; # build a sender record for this FT8 decoded message $txrcd .= ( pack( "c", length( $call)) . pack( "A*", $call) . pack( "N", $freq) . pack( "c", $snr) . pack( "c", 3) . pack( "A*", "FT8") . pack( "c", 1) . pack( "c", length( $grid)) . pack( "A*", $grid) . pack( "N", $time) ); # remove this FT8 decode from hash array delete $db{ $cb}; # decrement FT8 main decode counter $d--; # track size of UDP datagram of all FT8 decodes to be sent and test if full reached max limit # if yes - exit loop to wrap and send datagram if( ( length( $txrcd) ) >= $MAXDATA) { last; } } # end hash loop to build datagram # reset datagram size counter in bytes $ds = 0; # calculate the length of the record and determine padding to multiple of 4 bytes $txrcdlen = length( $txrcd)+ 4; $txrcdpad = 4 - ( $txrcdlen % 4); # create entire sender record with sender header and 00 padding $txrcd = ( pack( "H*", $txhdr) . pack( "n", ( $txrcdlen + $txrcdpad)) . $txrcd . pack( "x$txrcdpad") ); # create complete UDP datagram packet holding header, time, sequence number, random ID, # receive & send descriptions and receive & sender records $time = time(); $packet = ( pack( "H*", $header) . pack( "n", length( $rcvr) + length( $sndr) + length( $rxrcd) + length( $txrcd) + 16) . pack( "N", $time) . pack( "N", $seq++) . pack( "A*", $rid) . pack( "A*", $rcvr) . pack( "A*", $sndr) . pack( "A*", $rxrcd) . pack( "A*", $txrcd) ); # open UDP socket to PSKReporter my $sock = IO::Socket::INET->new( Proto => 'udp', PeerPort => $peerport, PeerAddr => $peerhost, ) or next; ## ) or die "Could not create socket: $!\n"; ## send datagram print $sock $packet; #print $packet; ## close socket $sock->close(); } # end of datagram creation/sending, loop if more FT8 decodes need to be sent in another datagram truncate $logfile, 0; undef %db; |
Bonjour,
J’ai utiliser le script d’envoie de spot cet automne avec succès.
Depuis deux jours, j’aimerais remettre en place le système et aucun des spots ne semble être pris en compte par pskreporter.
Le décodage fonctionne. Le log est bien alimenté.
Le host sur le port 4739 est accessible.
Utilisez-vous encore ce script actuellement et si oui fonctionne-t-il chez vous?
73 Pascal
Bonjour,
Désolé pour la réponse tardive.
Il se peut que le problème vienne de la version de python pour le calcul du shift.
J’ai fait les tests hier.
Je remettrais à jour dès que possible.
Il faut remplacer :
python -c "print float($centerfreq-$freq)/$samplerate"
par
python3 -c "print(float($centerfreq-$freq)/$samplerate)"
Sinon j’ai remarqué que cela dépend aussi des flux I/Q sur entrée droite ou gauche de la carte son (loopback ou réelle).
Pour inversé vous pouvez rempalcer:
python3 -c "print(float($centerfreq-$freq)/$samplerate)"
par
python3 -c "print(float($freq-$centerfreq)/$samplerate)"
et
csdr bandpass_fir_fft_cc 0 0.5 0.05
par
csdr bandpass_fir_fft_cc -0.5 0 0.05